
1. 图书销售管理系统为什么值得从零写一个先把这个项目的真实定位说清楚Spring Boot图书销售管理系统是Java后端开发里最经典的“练手求职面试课设毕设”三合一项目。它的业务边界非常明确——图书的商品管理、购物车、下单、库存扣减、订单查询再加一个后台管理界面。这套东西看着简单但把权限控制、事务回滚、异常处理、数据一致性问题全部串起来了恰好覆盖了企业开发里80%的常见场景。这套系统最适合三类人一是刚学完SSM或Spring Boot基础、想找个完整项目串联知识点的初学者二是准备校招或社招需要一个能讲清楚设计思路的项目往简历上写的求职者三是计算机相关专业做课程设计或毕业设计的同学。我当年就是靠一个类似的管理系统项目拿到的第一份后端offer面试官最在意的不是功能多少而是你对订单和库存这种“钱货相关”模块的理解深度。图书销售和普通商品销售有个很大的差异点图书有明确的品类、出版社、ISBN、库存批次而且往往存在“多册成套”的逻辑比如一套丛书共5册客户可能只买其中2册。再加上图书定价存在折扣策略、会员价和非会员价的差异导致订单金额计算不能简单地“单价乘以数量”。这些业务细节正是让这个系统区别于“玩具CRUD”的关键——无论你是为了面试还是为了课设把这些点做进去项目档次立刻不一样。另一个容易被忽视的点是这个项目非常契合Spring Boot的技术特性和生态。Spring Boot的自动配置能让数据源、MyBatis、Redis、事务管理器这些组件“开箱即用”让你把精力集中在业务代码上而不是去折腾繁琐的XML配置。同时Spring Boot天然适合前后端分离——后端纯接口前端用Vue或者直接渲染Thymeleaf都行。我在这篇里会以“后端接口RESTful风格”为主线兼顾模板渲染的方案两条路都给你踩平。提示如果你还在纠结“做成单体还是拆分微服务”我的建议是图书销售管理系统做成单体应用就是最优解。业务规模摆在那边微服务只会带来分布式事务、链路追踪等一堆你目前根本用不上的复杂度。单体应用配合模块化分包后期就算要拆成本也完全可控。2. 技术选型先想清楚再动手版本真的不必追新2.1 Spring Boot版本与JDK的搭配逻辑关于Spring Boot版本网上铺天盖地都是“用最新版”但我的实际经验是稳定优先、文档其次、生态再次。当前阶段最推荐的是Spring Boot 2.7.x系列配合JDK 8或JDK 11。为什么不直接上Spring Boot 3.x因为Spring Boot 3.0之后强制要求JDK 17并且javax包全部换成了jakarta很多老教程和依赖管理工具链都还没完全同步。如果你做课设、毕设或者面试项目卡在版本兼容问题上会消耗大量无效时间。Spring Boot 2.7.x是2.x系列的收官版本继承了多年的稳定性修复同时兼容性最好——MyBatis、PageHelper、Druid、Redis、JWT这些常用库的整合资料一搜一大把踩坑成本极低。JDK选择上JDK 8依然是国内大多数企业的“生产主力”也是面试题的高频背景。如果你的机器装了更高版本的JDK比如JDK 17或21在IDEA里直接把Project Structure的语言级别调到8即可配合Maven的compiler插件指定source和target完全不影响开发体验。2.2 持久层框架怎么定MyBatis还是JPA图书销售系统里存在大量的多表关联查询订单表关联订单明细、图书表关联库存、用户表关联收货地址而且很多查询带有动态条件按书名模糊搜索、按出版社过滤、按价格区间过滤。这种场景下MyBatis是更稳妥的选择。MyBatis的XML文件可以精细控制SQL一旦遇到复杂查询或者需要在SQL层面做性能优化你能直接在XML里改SQL改完热部署就能生效。JPA的强项是单表CRUD和跨数据库兼容但在这种以查询为核心的系统里它的复杂查询Specification/QueryDSL写起来反而绕。有一点需要提醒MyBatis的if动态SQL是日常用的最多的但务必在where标签里处理条件拼接否则很容易出现“多一个AND”的语法错误。注意MyBatis的Mapper接口和XML文件必须保证namespace完全匹配。我见过太多初学者把XML文件放在src/main/java目录下但没在pom.xml里配置resources导致启动时报“Invalid bound statement (not found)”——这个错常年霸榜Spring Boot MyBatis问题TOP3记住在build配置里加上resources resource directorysrc/main/java/directory includes include**/*.xml/include /includes /resource /resources2.3 其他关键组件的选型清单数据库选MySQL 5.7或8.0都可以推荐8.0因为默认字符集是utf8mb4对图书书名这种可能包含特殊符号的场景更友好。如果做课设没条件装MySQL用MariaDB或者阿里云的RDS免费额度也完全可以SQL方言基本一致。连接池选Druid因为它自带监控页面能直观看到SQL执行耗时、活跃连接数、慢查询统计。虽然HikariCP在Spring Boot 2.x里是默认连接池但Druid的监控面板对答辩和面试展示是加分项——你可以在面试时直接打开监控页证明你的系统做过性能观察。缓存方面引入Redis用来缓存首页轮播图、热销图书列表以及登录会话。有的初学者容易在这过度设计——所有查询都先查Redis但缓存命中率极低反而让代码更难维护。我建议只缓存两类数据一类是热点列表数据如首页推荐位另一类是验证码和会话。图书库存数据坚决不缓存因为库存变化直接关联订单量和金额缓存稍有不一致就可能超卖。前端方案上如果赶时间就直接用Thymeleaf做服务端渲染简单省事一个项目跑起来不依赖Node环境。如果作品集里想展示前后端分离能力就用Vue3Vite后端只出JSON接口。本篇以接口开发为主线模板渲染的思路我会在相关模块里附带说明。3. 项目结构与数据库设计分包的边界决定代码的寿命3.1 Maven工程的模块划分基于Spring Boot的图书销售系统我建议按“com.example.bookstore”作为基础包名下面依次分层。分层不追求极致的“干净整洁”而是要让不同职责的代码有明确归属。controller接收HTTP请求做参数校验调用service层返回统一的响应结果。service业务逻辑层处理订单状态机、库存扣减、事务边界。mapperdaoMyBatis的Mapper接口对应XML中的SQL。entity数据库表对应的实体类字段与表结构一一映射。dto接口入参和出参对象避免实体类直接暴露给前端。vo视图对象专门用于给前端展示的聚合数据。configSpring配置类如拦截器、跨域配置、Redis配置。common统一返回结果、全局异常处理器、常量类、工具类。utilsJWT生成解析、MD5加密、验证码生成等工具。3.2 核心表的字段设计与关联关系图书销售系统的表不宜太多核心六张足矣用户表、图书表、购物车表、订单表、订单明细表、库存表。再加两个辅助表图书分类表、轮播图表给首页展示用。先看用户表字段里我刻意不存“明文密码”存password加密后的值同时加一个status字段用于封号/解封。设计这类基础表时一定要留足扩展字段比如用户表加一个level字段表示会员等级后续算折扣价时直接用不用再改表结构。图书表的字段设计要特别考虑“多规格”问题。一个图书有ISBN、书名、作者、出版社、出版日期、定价、折扣价、封面图URL、简介、分类ID。有的图书有“库存量”但库存应该独立成表——因为库存和图书是一对一但库存的变更历史需要记录进货、销售、退货放在图书表里会让每次库存变更都要UPDATE图书表并发场景下锁粒度太大。订单表是整个系统最重要的表。它的字段包括订单号、用户ID、订单总金额、实付金额、优惠金额、收货人姓名、收货人电话、收货地址、订单状态、创建时间、支付时间、发货时间。订单号建议用时间戳随机数的形式生成不要用数据库自增ID因为订单号需要在业务上唯一并且要防止通过订单号猜测销量。订单明细表关联订单表和图书表记录下单时的“快照”——书名、单价、数量、小计金额。为什么要有快照因为图书表的价格是可变的如果用户下单后管理员改了价格订单明细如果直接关联图书表价格字段那用户订单里的历史价格也会被改动这是绝对不允许的。这是很多初学者容易漏掉的点。3.3 订单状态与库存扣减的边界案例订单状态的流转是最容易出错的地方。我的状态机定义是待支付0已支付/待发货1已发货2已完成3已取消4售后/退款中5这里有一个必须明确的边界当用户点击“取消订单”时如果订单状态是“待支付”直接取消并回滚库存如果已经是“已支付”不能直接取消要走退款流程。这个判断在service层必须有状态机校验不能在controller就用if-else乱写。我见过一个项目里订单状态用int类型状态判断散落在各个方法中后来需求加了一个“待评价”状态改了一周代码全是因为状态边界不清晰。库存扣减的时机也很关键——是用户下单时扣减还是支付成功后再扣减这两种方案各有道理。下单即扣减能防止超卖但用户下单不支付会积压库存支付后扣减用户体验好但并发高时超卖风险大。我的建议是图书销售业务相对低频、高客单应该选择“支付成功后扣减库存”并配合“预占库存”的补偿机制——下单时记录预占数量但不真正扣减库存订单超时未支付时将预占数量回滚支付成功后把预占数量转成实际扣减。这套逻辑看起来复杂但能同时兼顾库存准确性和用户体验。提示库存扣减必须使用带条件的UPDATE语句而不是先SELECT再UPDATE。经典写法如下UPDATE t_book_stock SET stock stock - #{num} WHERE book_id #{bookId} AND stock #{num}若返回影响行数为0说明库存不足整个下单流程直接抛出业务异常。这个方式能避免并发场景下的“幽灵库存”。4. 核心功能模块实现购物车、订单、支付的实战细节4.1 用户登录与权限控制JWT的完整落地图书销售系统需要区分管理员和普通用户两种角色最简单的方案是在用户表中加一个role字段0为普通用户1为管理员然后通过Spring Boot的拦截器实现接口级权限控制。登录接口的流程是这样的用户输入账号密码后端用MD5密码加盐校验校验通过后生成一个JWT Token返回给前端。后续所有请求都在Header里带上Authorization: Bearer token。拦截器负责解析Token获取用户ID和角色存入ThreadLocal或请求属性中供后续业务逻辑使用。JWT的密钥要配置在application.yml里并通过Value注入不要在代码里写死。Token的有效期可以根据场景设定普通用户会话建议24小时管理员后台建议2小时。JWT有一个天然缺陷是“无法主动失效”所以做退出登录时前端要主动清除本地Token有更高安全诉求时再配合Redis黑名单。拦截器实现权限控制时要注意放行路径/api/user/login、/api/user/register、/api/book/list、/api/book/detail这些无需登录的接口要放在白名单里。管理端接口以/api/admin/开头必须校验角色为管理员否则返回403。这块我见过很多项目在Interceptor里把路径写错导致用户能直接访问管理接口这种安全漏洞在答辩时会被老师或面试官直接扣分。4.2 购物车模块合并购物车与价格回显购物车本质是“用户会话中的临时选品集合”存Redis还是存MySQL都可以。如果存Redis优势是读快、性能好劣势是Redis宕机会丢。如果存MySQL可靠性高但每次加购都要写库。我的做法是登录用户把购物车存MySQL未登录用户购物车存Redis用临时key标记登录后把Redis中的临时购物车合并到MySQL。这样既保证数据不丢也能兼顾匿名用户的体验。购物车接口至少要有这几个加购、修改数量、移除、查询购物车列表。在查询购物车列表时最重要的不是返回购物车表里存的数量和单价而是实时关联查询图书的当前价格——因为图书可能刚被管理员改过价购物车里的“小计”必须以当前价格重新计算并同时把“原价”和“折扣价”回传给前端让客户能看到优惠了多少。这里有个细节购物车表不要存储冗余的图书名称、封面图等字段这些字段通过图书ID关联查询即可。否则图书信息一改购物车里的数据就陈旧了。4.3 下单流程校验、锁、扣库存的编排下单是整个系统最复杂的业务流程步骤多、依赖强需要放在一个事务里。标准的段落是这样的接收下单请求从购物车勾选的商品列表、收货地址ID、备注。校验用户登录状态、收货地址是否存在。校验图书是否在售、库存是否足够。计算订单金额遍历购物车明细用当前图书价格乘以数量累加总金额。生成订单号插入订单表和订单明细表。扣减库存使用stock #{num}的条件更新。清空已下单的购物车商品。返回订单号。这几步必须在一个事务里执行。具体做法是在service方法上标注Transactional(rollbackFor Exception.class)并设置事务传播行为为REQUIRED。有一个常见坑是直接抛出RuntimeException时事务才回滚如果代码里捕获了异常忘记抛出事务不会自动回滚。我的习惯是自定义一个BusinessException在事务方法内任何地方失败都直接抛出它让全局异常处理器统一捕获。注意如果选择在支付成功后再扣库存下单阶段产生的是“预占记录”。这时候在下单事务里不要真正执行库存扣减的UPDATE而是往预占库存表中插入数据等到支付回调时再执行扣减。这个决策一定要在设计阶段定清楚不然后期改起来会牵连到订单列表、对账、退款等多个模块。4.4 订单支付回调逻辑的稳定性设计支付环节在学校项目里通常对接的是支付宝沙箱或微信支付沙箱但如果你只是为了演示功能也可以做一个“模拟支付”的开关——前端的“立即支付”按钮直接调后端一个“MockPay”接口后端把订单状态从待支付改成已支付。这样省去真正对接支付宝的繁琐流程同时把支付回调和幂等性的概念提前练习起来。真实对接收银台时核心是“异步通知处理”。支付宝会主动POST一个回调到你的接口这个回调不能保证只发一次所以接口必须具备幂等性——重复收到同一个支付通知不能重复更新订单状态。做法是在回调处理里先查询订单当前状态如果已经是已支付直接返回“success”字符串不再执行状态流转。还有一点支付回调里拿到的金额必须和系统里订单的实付金额比对不一致就返回失败防止中间人篡改。这种细节虽然在学校项目里不太会被攻击但在面试中能讲出来就是明显的加分项。5. 管理后台图书维护、订单处理与统计报表5.1 管理员端的权限和界面拆分管理后台和前台用户端建议接口完全分离。管理端的接口统一放在/api/admin/前缀下拦截器单独校验管理员角色。前端页面在Vue项目里可以拆成两个路由模块一个/user一个/admin两个模块在路由守卫里分别校验登录状态和管理员身份。管理后台的核心功能是图书管理上架、下架、改价、库存调整、批量导入。订单管理查看订单列表、订单详情、订单发货、订单取消、退款处理。用户管理用户列表、封号/解封、重置密码。数据统计每日销售额、热销图书Top10、品类销量分布。图书批量导入是一个很常见但常被忽略的功能实现方案是后端接收上传的Excel文件用EasyExcel解析并校验数据逐行插入图书表。批量导入一定要做读后校验——不能读取一行就插入一行而是先全部读入内存校验全部通过后再批量插入。否则Excel中间某行格式错误时前面的行已经入库处理起来就很被动。5.2 订单发货与退款的状态校验发货操作的业务逻辑比较简单但状态校验不能省只有“已支付”状态的订单才能发货发货后状态变成“已发货”并记录物流单号。调用发货接口时service层先查询订单状态如果不是已支付直接抛异常“当前订单状态不可发货”。退款流程稍微复杂一点。如果订单是“已支付”未发货用户申请退款后管理员审核通过执行退款的同时要回滚库存——因为之前支付时已经扣减了库存。如果订单是“已发货”但用户拒收则要走到退货流程等收到退货后再回滚库存。这个逻辑用状态机管理会清晰很多支付-发货-完成是正向链路支付-取消-退款、发货-退货-退款是反向链路。反向链路里库存回滚的时机一定要和“物流是否已真实退回”关联不能简单地在退款通过时就回滚库存。5.3 销售统计与报表SQL聚合的典型写法统计报表这块很多同学喜欢用Java代码在内存里做聚合表数据量小的时候确实能跑但效率极低不说还容易出NullPointer。更专业的做法是直接在SQL层用GROUP BY和聚合函数完成。比如查询“每日销售额”SELECT DATE(create_time) AS day, SUM(actual_amount) AS total_amount, COUNT(*) AS order_count FROM t_order WHERE order_status 3 AND create_time BETWEEN #{startTime} AND #{endTime} GROUP BY DATE(create_time) ORDER BY day查询“热销图书TOP10”的时候要关联订单明细表因为订单金额在订单主表销量在明细表SELECT t.book_id, b.book_name, SUM(t.quantity) AS total_sold FROM t_order_item t LEFT JOIN t_book b ON t.book_id b.id LEFT JOIN t_order o ON t.order_id o.id WHERE o.order_status IN (2, 3) AND o.create_time #{startTime} GROUP BY t.book_id, b.book_name ORDER BY total_sold DESC LIMIT 10有一个关键点统计销量时订单状态必须过滤已取消和退款的订单不能计入销量否则报表虚高。这个过滤条件也可以通过定时任务定期跑批并写入统计表但课设阶段直接实时查询即可加上索引后百万级数据也没压力。6. 常见问题与避坑清单堵住初学者最容易翻车的12个坑6.1 项目启动与配置问题Q1启动时报Failed to configure a DataSource原因通常是pom引入了spring-boot-starter-data-jpa或spring-boot-starter-jdbc但application.yml里没配置数据源。解决方法是检查配置文件里的spring.datasource.url、username、password是否齐全驱动是否引入。Q2MyBatis的Mapper接口找不到XML这个前面说过大概率是XML没有被Maven打包进入classpath。解决方式就是配置resources包含**/*.xml然后刷新Maven工程再清理target目录后重启。Q3Lombok的Data生成的getter和setter报错高版本JDK环境下Lombok需要升级到与JDK匹配的版本。检查pom里Lombok版本如果用的JDK17Lombok必须用1.18.30以上。6.2 业务逻辑问题Q4订单超时未支付但库存一直被预占解决方式是用Spring Boot的定时任务每分钟扫描一次订单表把超时15分钟仍未支付的订单状态置为已取消并回滚预占库存。定时任务要加分布式锁避免多实例部署时重复执行。Q5购物车并发加购导致数量错乱使用数据库的唯一索引(user_id, book_id)加购时先查询再更新更新语句用quantity quantity #{num}避免覆盖式赋值。Q6金额计算出现精度丢失绝对不要用double或float计算金额。使用BigDecimal构造时优先用new BigDecimal(String)或BigDecimal.valueOf(double)不要直接用new BigDecimal(double)否则会引入二进制浮点误差。Q7事务没有回滚原因不外乎三种异常被吞掉、事务方法被同类的其他方法内部调用导致代理失效、方法不是public导致Spring代理不生效。事务生效的关键是通过Spring容器注入的bean调用不能在同类内部用this.xxx()调用。Q8分页查询数据量过大导致接口卡顿用PageHelper或MyBatis-Puls分页插件配合LIMIT偏移量分页。表数据量大时再考虑优化为游标分页WHERE id #{lastId} ORDER BY id LIMIT #{size}。6.3 部署与运维问题Q9Spring Boot项目打包后运行提示“no main manifest attribute”检查pom是否引入了spring-boot-maven-plugin。这个插件会把项目打成一个可执行Jar包并生成MANIFEST.MF中的Main-Class信息。缺少该插件时打出来的Jar只是一个普通的依赖包。Q10生产环境上传的图片访问不到解决方式有两种一是从“静态图片路径”出发用本地磁盘存储并配置虚拟路径映射二是把图片传到对象存储服务如阿里云OSS、MinIO我把OSS的调用写在service层开发环境用本地磁盘生产环境切OSS通过一个接口切换运维成本极低。Q11Redis连接失败导致系统启动不了Spring Boot默认在启动时会连接到Redis并初始化连接池如果Redis没启动应用会直接启动失败。解决方式是给Redis操作封装一层工具类并配置连接重试和超时时间避免Redis故障导致整个下单流程不可用。生产上即便Redis挂了也要保证MySQL能支撑读请求这是微服务降级思想的单体简化版。Q12跨域请求被拦截前后端分离后必定遇到这个问题。在Spring Boot里写一个全局CORS配置类配置允许的源、方法、请求头重点是allowedOrigins不能写*同时要搭配allowCredentials(true)否则浏览器会直接拦截。7. 项目部署与扩展方向做完不是终点7.1 打包与部署的最小可行方案本地开发完成后部署无非两种路线传统服务器部署和Docker部署。传统部署就是Maven打包生成可执行Jar放到服务器上执行mvn clean package -DskipTests java -jar book-store-0.0.1-SNAPSHOT.jar --spring.profiles.activeprod这里的关键是profile的切换。开发环境用application-dev.yml生产环境用application-prod.yml。数据库密码这种敏感信息不要写在配置文件里用环境变量注入比如password: ${DB_PASSWORD:root}这样即使代码仓库泄露数据库密码也不会跟着泄露。Docker部署则需要写一个Dockerfile把这个系统做成镜像。Dockerfile要遵循单进程原则——只需运行Java进程不需要把MySQL、Redis也打进同一个容器。MySQL和Redis用Docker Compose单独起或者直接用云数据库。下面是一个生产可用的Dockerfile模板FROM maven:3.8.4-jdk-8 AS build WORKDIR /app COPY pom.xml . RUN mvn dependency:go-offline COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:8-jre-alpine WORKDIR /app COPY --frombuild /app/target/book-store-0.0.1-SNAPSHOT.jar app.jar EXPOSE 8080 ENV SPRING_PROFILES_ACTIVEprod ENTRYPOINT [java, -Xms512m, -Xmx512m, -jar, app.jar]打包成镜像后通过Docker命令启动即可。如果服务器上还开着防火墙记得放行8080端口。7.2 这个系统还能扩展哪些方向做完基础版本后如果你想提升项目深度以下几个方向值得考虑一是接入消息队列。把“下单成功后发送短信通知”和“库存不足时触发补货提醒”做成异步消息用ActiveMQ或RabbitMQ异步解耦。Spring Boot整合ActiveMQ的配置并不复杂但能有效提升下单接口的响应速度。二是引入搜索引擎。图书搜索如果只靠MySQL的LIKE查询数据量大时会很吃力。可以把图书的标题、作者、出版社字段同步到Elasticsearch用IK分词做中文检索配合Spring Boot的Elasticsearch客户端实现高效搜索。这个扩展非常适合毕设展示因为它能直观体现“查询性能提升”。三是加入数据看板。用ECharts前端图表展示订单趋势、用户增长、图书品类分布。后端提供聚合接口前端画出折线图、饼图、柱状图视觉冲击力强答辩效果非常好。四是上线多级缓存。热点图书详情页用Caffeine做本地缓存加上Redis做分布式缓存缓存未命中时才查询数据库。这个优化讲起来很简单但真正落地时要解决缓存穿透、缓存雪崩、缓存一致性三个经典问题。这三点是面试必问做进项目后你能讲得比背概念的人深刻得多。7.3 代码管理、文档与自测建议很多同学做课设、毕设只管写代码不管代码规范等到答辩前几天才发现代码里一堆调试用的System.out注释少得可怜甚至无法在导师的设备上启动。我建议从第一天就把Git仓库初始化好每次完成一个模块就提交一次提交信息写清楚。答辩前把README写好把启动步骤、数据库初始化脚本、测试账号、Swagger接口文档地址全部整理进去。SwaggerSpringfox或springdoc强烈建议集成它能自动扫描Controller生成接口文档调试HTTP接口非常方便。前端开发对接时也不用来回问接口参数直接打开Swagger页面就能看到完整的请求示例。给项目预留一个“一键启动”体验——别人克隆代码后执行一个初始化脚本就能把数据库表结构和样例数据造出来然后启动项目即可看到效果这会极大提升项目评审印象。代码自测建议用JUnit5加MockMvc写几个核心接口的单元测试至少覆盖用户登录成功/失败、图书列表分页、下单成功/库存不足、订单取消。不需要追求覆盖率但核心链路有自动化测试说明你具备基本的质量意识。8. 写在最后这个项目真正教会你的东西如果把图书销售管理系统当作一个“增删改查Demo”那它确实没什么稀奇。但如果把它当成一座桥——桥梁的一端是书本上的Spring Boot用法另一端是企业里的真实业务场景——那么这座桥的价值就大得多了。我见过太多简历上写着“精通Spring Boot”但一聊到订单超时未支付怎么处理、库存超卖如何避免、退款时库存什么时候回滚就支支吾吾答不上来。这些问题绝不是背八股能解决的必须亲手在系统里踩过坑、改过逻辑、看过线上数据才能形成真正的理解。根据自己的经验有个建议是做这个项目的过程里尽量别直接抄网上的开源代码。哪怕是照着别人的项目一步步敲也要把每一步“为什么会这样设计”想清楚。等你把订单状态机、库存预占、事务回滚、幂等回调这些问题都完整走过一遍后面面试官再问项目你脑子里全是实战画面回答自然流畅远胜照本宣科。如果时间充裕把这个项目拆成两个模块跑一遍前后端分离部署——后端Jar跑在服务器前端Vue打包后Nginx托管再配一个HTTPS证书。等整套流程都打通的时候你的项目就不只是“能运行”而是真正“能上线”。到那时候这份经验的含金量跟只会用IDEA按“运行”按钮的人完全不在一个量级。