ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

基于SpringBoot+Vue的房屋租赁管理系统全栈部署实践

基于SpringBoot+Vue的房屋租赁管理系统全栈部署实践 做房屋租赁管理系统这类项目很多人第一反应是“搞个表格增删改查”但真正把“房东发房源、租客下单、管理员审核、合同生成”这一整套业务跑通还要能部署上线给客户演示难度并不小。我这次基于SpringBoot Vue MyBatis MySQL这套组合完整实现了前后端分离的房屋租赁管理系统从数据库设计、后端接口开发到Vue前端页面联调再到本地打包和服务器部署整个过程踩了不少坑也沉淀了一些非常实用的经验。这篇文章就把整个项目的核心设计思路、关键代码实现、部署细节和排查技巧完整梳理一遍无论是用来做毕业设计、自学项目实战还是接私活做演示项目都很有参考价值。1. 项目整体架构设计1.1 为什么这套系统要选择前后端分离如果你只是做一个内部管理后台用传统的Thymeleaf模板渲染其实更快把Java代码和页面写在同一个工程里开发成本确实低。但房屋租赁系统有一个明显的特点使用角色多、访问场景杂。租客可能用手机浏览器访问房东可能用PC管理房源管理员需要看数据看板未来还可能扩展小程序端。如果所有页面都靠后端渲染每来一个端就要改一套模板维护成本会直线上升。前后端分离的核心价值在于后端只需要专注输出数据前端负责呈现和交互。后端把接口写好返回JSON数据前端可以用Vue做桌面端页面也可以抽一套接口给小程序的H5页面复用。我做这套系统时后端接口设计完全基于RESTful风格前端只需要关心怎么调用接口、怎么渲染数据两边各管各的联调起来非常清爽。另一个实际好处是开发效率。Vue有热更新改完页面代码浏览器自动刷新后端接口用Postman测好之后前端直接对接不需要反复重启Tomcat等页面渲染。多人协作时前端攻城狮和后端DBA可以并行开发只要提前约定好接口文档格式就行。1.2 技术选型SpringBoot、Vue、MyBatis、MySQL各自的角色这套技术栈在今天可以说是Java Web开发的标准组合每个组件的选型都有明确的理由技术组件在本项目中的角色为什么选它SpringBoot后端基础框架负责接口发布、业务编排、依赖管理简化配置内嵌Tomcat一个jar包就能跑起来极大降低部署成本Vue前端框架构建页面组件和交互逻辑轻量、上手快、生态成熟Element UI配合做后台管理页面效率极高MyBatis持久层框架处理SQL与Java对象的映射相比JPASQL是显式可控的像房屋搜索这种复杂查询和排序能精细优化MySQL数据存储保存用户、房源、订单、合同等核心数据开源免费、使用面广招聘市场大量岗位要求熟悉MySQL项目通用性强这套组合还有一层考虑是人才匹配度。招聘平台上搜Java开发岗位SpringBoot、Vue、MyBatis、MySQL几乎是标配关键词很多求职者看到这套技术栈就有兴趣上手做出来的项目写在简历上认可度也高。如果你用一些冷门的ORM框架或者国内小众前端框架虽然技术含量不低但面试官不一定熟悉反而解释成本高。2. 数据库与业务模型设计2.1 房屋租赁的核心业务角色与流程开工之前一定要把业务角色理清楚否则后面做权限控制、状态管理会乱成一锅粥。这套房屋租赁系统里有三类角色租客注册登录后搜索房源、查看房源详情、下单租房、查看自己的订单状态。房东发布房源、管理自己名下的房源上下架、查看租客的租房订单、确认合同。管理员审核房东发布的房源、管理用户、统计平台房源和订单总量。核心业务流转是这样的房东发布房源 → 管理员审核通过 → 房源上架展示 → 租客浏览并下单租房 → 租客支付本项目用模拟支付 → 双方签署合同 → 租房生效 → 租期到期或提前退租 → 订单关闭。这里有个很关键的设计点房源、订单、合同的状态要联动但又要独立维护。比如房源状态有“待审核”、“上架中”、“已出租”订单状态有“待支付”、“已支付”、“履约中”、“已完成”、“已取消”。租客支付成功后订单变成“已支付”此时房源状态要同步变为“已出租”防止其他人重复下单。这部分我放在事务里处理确保状态一致性。2.2 核心表结构设计数据库设计是项目的根基表设计不好后面写SQL会非常痛苦。我按业务模块拆分成四张核心表用户表、房源表、订单表、合同表。用户表user关键字段CREATE TABLE user ( id INT NOT NULL AUTO_INCREMENT, username VARCHAR(50) NOT NULL COMMENT 用户名, password VARCHAR(100) NOT NULL COMMENT MD5加密后的密码, phone VARCHAR(20) DEFAULT NULL COMMENT 手机号, role TINYINT NOT NULL DEFAULT 0 COMMENT 角色0租客 1房东 2管理员, status TINYINT NOT NULL DEFAULT 1 COMMENT 账号状态0禁用 1正常, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT用户表;密码不建议明文存储我用MD5配合盐值做了加密。生产项目建议用BCrypt这个项目为了演示方便用MD5但文章里要提醒读者升级到BCrypt处理。房源表house是核心业务表字段要覆盖展示和检索两方面的需求CREATE TABLE house ( id INT NOT NULL AUTO_INCREMENT, landlord_id INT NOT NULL COMMENT 房东用户ID, title VARCHAR(100) NOT NULL COMMENT 房源标题, cover VARCHAR(255) DEFAULT NULL COMMENT 封面图URL, images TEXT COMMENT 多张图片URL用逗号分隔, area DECIMAL(10,2) NOT NULL COMMENT 面积平方米, price DECIMAL(10,2) NOT NULL COMMENT 月租金元, address VARCHAR(255) NOT NULL COMMENT 小区地址, city VARCHAR(50) DEFAULT NULL COMMENT 城市, description TEXT COMMENT 房源描述, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0待审核 1上架中 2已出租 3已下架, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT NULL, PRIMARY KEY (id), KEY idx_landlord (landlord_id), KEY idx_status (status) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT房源表;查询性能上房源表一定不要只依赖主键。租客搜索房源时通常会用“城市 租金区间 面积”作为过滤条件所以我给status和landlord_id都建了索引。随着房源数据增长到几十万条时这些索引会明显提升分页查询速度。订单表order的核心字段必须记录下单快照因为房源价格可能调整合同要按下单时的价格签署CREATE TABLE rent_order ( id INT NOT NULL AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL COMMENT 订单编号, house_id INT NOT NULL, tenant_id INT NOT NULL COMMENT 租客ID, landlord_id INT NOT NULL COMMENT 房东ID, month INT NOT NULL COMMENT 租期月数, unit_price DECIMAL(10,2) NOT NULL COMMENT 下单时月租金, total_price DECIMAL(10,2) NOT NULL COMMENT 总金额, status TINYINT NOT NULL DEFAULT 0 COMMENT 状态0待支付 1已支付 2履约中 3已完成 4已取消 5已退租, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, pay_time DATETIME DEFAULT NULL COMMENT 支付时间, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no), KEY idx_tenant (tenant_id), KEY idx_house (house_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT租房订单表;跨表关联时外键约束我建议在表设计阶段保留但实际生产环境如果并发量高可以考虑逻辑外键代替物理外键。演示项目用物理外键更直观方便面试时解释表关系。2.3 订单状态机与房源状态的联动很多初学者做这类系统喜欢用一堆if/else去判断状态结果逻辑越写越乱。更规范的做法是先定义状态机再写代码。我只允许以下状态流转发生房源状态待审核(0) → 上架中(1)待审核(0) → 已下架(3)上架中(1) → 已出租(2)已出租(2) → 已下架(3)订单状态待支付(0) → 已支付(1) → 履约中(2) → 已完成(3)以及待支付(0) → 已取消(4)、履约中(2) → 已退租(5)状态机一旦定下来后端接口里就不要随意越级修改状态。我在Service层里写了一个状态校验方法每次更新前先查当前库里的状态再判断是否允许跳到目标状态不允许则抛出业务异常。这样即使前端误操作、接口被恶意调用后台也不会产生脏数据。3. 后端开发SpringBoot MyBatis实战解析3.1 后端工程结构与分层思路后端工程我用的是标准Maven多模块结构不过这个体量不需要过度拆分单模块按包名分层就够了src/main/java/com/example/rent ├── config // 全局配置类跨域、拦截器、MyBatis配置 ├── controller // 接口层接收请求返回统一结果 ├── service // 业务层处理业务逻辑与事务 ├── mapper // 数据访问层MyBatis的Mapper接口 ├── entity // 数据库实体类 ├── dto // 请求/响应参数对象 ├── common // 通用类统一返回结果、异常处理、工具类 └── RentApplication.java // 启动类分层核心原则是Controller不写业务代码Service不写SQLMapper只做数据访问。很多毕设代码Controller里直接写一大坨业务逻辑虽然没有链路问题但后续想加一个角色权限控制会发现几乎无从下手。统一返回结果是一个容易被忽视但非常重要的设计。我定义了一个R类public class R { private Integer code; // 200成功500业务异常401未登录 private String message; private Object data; public static R ok() { return new R(200, success, null); } public static R ok(Object data) { return new R(200, success, data); } public static R error(String message) { return new R(500, message, null); } }前端axios拦截器拿到code后统一处理200走成功逻辑401跳登录页500弹出错误提示。这个约定越早定越好前后端联调时能省下很多无意义的沟通。3.2 房屋分页搜索接口动态SQL与排序房屋搜索是整个系统的核心接口要求支持关键词模糊查询标题、地址、租金区间过滤、面积过滤、城市过滤还要支持按租金排序和按时间排序。这种查询条件动态变化的需求正是MyBatis动态SQL最擅长的场景。Mapper XML里的核心实现select idpageHouse resultTypecom.example.rent.entity.House SELECT * FROM house where if testkeyword ! null and keyword ! AND (title LIKE CONCAT(%, #{keyword}, %) OR address LIKE CONCAT(%, #{keyword}, %)) /if if testcity ! null and city ! AND city #{city} /if if testminPrice ! null AND price ![CDATA[ ]] #{minPrice} /if if testmaxPrice ! null AND price ![CDATA[ ]] #{maxPrice} /if if teststatus ! null AND status #{status} /if /where ORDER BY choose when testsort price_ascprice ASC/when when testsort price_descprice DESC/when when testsort time_desccreate_time DESC/when otherwisecreate_time DESC/otherwise /choose /select排序这里我要重点说一个很多人踩过的坑ORDER BY后面的字段不能直接用#{}占位符因为MyBatis对预编译参数会加引号你拼进去price DESC会变成字符串字面量SQL直接报错。有些教程让你用${sort}硬拼这确实能跑但存在SQL注入风险用户传一个恶意字段名就能炸库。我推荐的做法是上面这种白名单映射在Java代码里定义允许的排序字段前端传参后先做个格式校验再映射到固定SQL片段既安全又清晰。分页选择上我一开始用手写的LIMIT分页因为数据量不大完全够用。但如果你开发时用了PageHelper要特别注意它和自定义动态SQL一起使用时的count查询优化问题处理不好会导致分页总数统计异常。3.3 JWT登录认证与权限控制登录认证这块我没用传统的Session方案而是选择JWT。原因很直接前后端分离后后端接口可能被多个前端调用Session共享在分布式部署时很麻烦JWT的Token机制天然解决了这个问题。实现逻辑三步走用户登录成功后后端生成一个Token里面封装用户ID、用户名、角色用HMAC密钥签名设置有效期我设置了24小时。前端拿到Token后存到localStorage每次axios请求在拦截器里自动加上Authorization: Bearer xxx请求头。后端写一个拦截器放行登录、注册、房源列表公开接口其余接口统一解析Token解析失败直接返回401。唯一要提醒的是JWT最大的弱点是无法注销用户改密码或者被管理员封号后已签发的Token在有效期内依然能用。我项目里通过用户表的status字段做了二次校验每次请求拦截器解析出用户ID后查一下账号是否被禁用禁用就拒绝访问。这个思路在面试中很加分体现出你对安全的深入理解。权限控制我是通过自定义注解RequireRole实现的在需要房东权限的接口上加RequireRole(landlord)拦截器里解析注解并根据当前登录用户的角色决定是否放行。这样比在方法内部写死角色判断要优雅得多。3.4 MyBatis开发中的几个高频坑第一个坑是实体类字段与数据库字段映射不上。数据库字段用下划线命名create_timeJava字段用驼峰命名createTimeMyBatis默认不能自动映射结果查出来的字段全是null。解决办法是在application.yml里开启下划线转驼峰配置mybatis: configuration: map-underscore-to-camel-case: true这个配置加上后只要数据库字段和Java字段风格一致就不需要每个实体都写一大串resultMap了。第二个坑就是热词里经常有人问的mybatis条件不生效。常见原因有几个if标签的test条件写错比如判断String非空用了! null而不是! null and ! 或者Java方法参数没有加Param注解XML里引用参数名对不上。我这边统一规范方法参数多于一个的Mapper接口必须明确注解参数名避免编译时参数名丢失导致绑定失败。第三个坑是SQL日志打印问题。排查SQL时如果控制台没有任何SQL输出很难定位问题。我推荐在配置里加上mybatis: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl这样执行的每条SQL、参数、结果数都会打印到控制台开发阶段非常有帮助。生产环境再关掉即可。4. 前端开发Vue Element UI实战解析4.1 Vue工程初始化与环境配置前端工程我用的Vue CLI创建Node.js版本建议用16.x或18.x LTS。很多人在这个环节卡住原因基本是Node版本和依赖包版本不匹配。比如Node 20配旧版node-sass会编译失败Vue CLI 5配某些webpack插件也有兼容问题。依赖安装常用的命令npm install -g vue/cli vue create rent-frontend cd rent-frontend npm install element-ui axios vue-router vuex --save在国内网络环境npm直接安装依赖经常卡在下载环节。我的建议是提前把仓库源切换成淘宝镜像npm config set registry https://registry.npmmirror.com实测下来切源之后安装速度能从几十分钟降到两三分钟。如果项目里已经生成了package-lock.json安装时加上--registry参数或者直接删掉锁文件重新装避免锁文件里的旧地址拖慢速度。工程目录里我习惯把页面组件、公共组件、路由、状态管理分开src ├── api // 接口请求封装模块 ├── assets // 静态资源 ├── components // 公共组件搜索栏、分页组件等 ├── router // vue-router路由配置 ├── store // vuex状态管理 ├── views // 页面组件登录页、注册页、房源列表、房源详情、个人中心、后台管理 ├── App.vue ├── main.js └── vue.config.js4.2 路由守卫与axios请求封装路由守卫的核心作用是前端权限控制。用户没登录可以访问房源列表和详情但进入“个人中心”、“我的订单”这些页面之前必须检查本地是否有Token。router.beforeEach((to, from, next) { const token localStorage.getItem(token) if (to.meta.requiresAuth !token) { next({ path: /login, query: { redirect: to.fullPath } }) } else { next() } })这段代码虽然简单但关键点是redirect参数的保存。用户被拦截去登录页登录成功后应该自动跳回原本想去的页面而不是每次都回首页这个小细节体验差异很大。axios请求封装要统一处理三件事请求头注入Token、响应码统一处理、错误提示统一弹出。const service axios.create({ baseURL: /api, timeout: 10000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers[Authorization] Bearer token } return config }) service.interceptors.response.use( response { const res response.data if (res.code ! 200) { Message.error(res.message || 系统异常) if (res.code 401) { router.push(/login) } return Promise.reject(new Error(res.message)) } return res }, error { Message.error(error.message || 网络异常) return Promise.reject(error) } )4.3 房源发布与订单支付的关键页面逻辑房源发布页面前端要处理图片上传。Element UI的Upload组件可以传图片到后端的文件上传接口我们项目里用的是本地文件夹存储Nginx做静态映射访问。图片上传成功后会返回一个URL前端保存到表单的images字段里提交房源时一并传给后端。后端文件上传接口有一个安全细节校验文件类型和大小。我限制了图片只能jpg、png、webp格式大小不超过2MB服务端再次校验避免恶意上传可执行文件。订单支付页面做的是模拟支付。真实场景对接微信或支付宝需要商户号和三方SDK演示项目里我简化成用户点击“确认支付”前端调后端支付接口后端设置订单为“已支付”房源状态同步改成“已出租”订单状态流转到“已支付”。页面上再做5秒倒计时反馈增加真实感。这里前端要注意的一个常见问题是重复提交。用户快速双击支付按钮会连续调两次接口可能导致订单状态异常。我的解决方案是按钮加loading状态提交后立即禁用防止重复点击。5. 环境搭建与部署上线从本地到服务器5.1 本地开发环境版本搭配技术栈的版本搭配直接决定了你是否会踩坑。我在这套项目里用的版本组合是软件推荐版本号备注JDK1.88u201稳定可靠生态兼容性最好Maven3.6.3不要用太新的3.9.x配旧项目可能有兼容问题SpringBoot2.7.18最后支持JDK8的版本线Node.js18.x LTS兼容Vue CLI 5MySQL8.0.x性能好但要注意驱动参数Vue CLI5.x对应Webpack 5很多人问“springboot版本太高”怎么办。核心矛盾是SpringBoot 3.x强制要求JDK17而你本机是JDK8跑3.x直接启动失败。解决方案有两个一是把JDK升到17但很多老项目依赖比如一些MyBatis增强工具包不兼容二是我推荐的老老实实选SpringBoot 2.7.x它是2.x系列的最后一个版本安全补丁也比较全演示项目和面试评价都不吃亏。5.2 MySQL安装与数据库初始化MySQL 8.0在Windows上安装网上教程质量参差不齐。如果你不想用安装包向导推荐直接用ZIP解压方案可控性更强# 下载 mysql-8.0.xxx-winx64.zip 解压后执行 mysqld --initialize-insecure # 初始化数据目录root空密码 mysqld -install # 注册Windows服务 net start mysql # 启动服务 mysql -u root -p # 密码直接回车进入初始化之后SQL执行文件准备好包含建库、建表、初始数据三个脚本依次执行即可。有一个非常关键的细节MySQL 8的驱动参数。JDBC连接串必须配置多个参数否则会遇到各种连接失败spring: datasource: url: jdbc:mysql://localhost:3306/rent?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrue username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver其中allowPublicKeyRetrievaltrue是MySQL 8的典型坑。如果缺少这个参数连接时会报Public Key Retrieval is not allowed这是MySQL 8默认的caching_sha2_password认证方式导致的用了mysql_native_password或sha256_password以外的插件就必须配置这个参数。字符集方面数据库连接串里characterEncodingutf8是JDBC层保持UTF-8传输真正保证中文不乱码的关键是建表时指定utf8mb4。我建库默认排序规则都用utf8mb4_general_ci这样中文排序和显示不会出错。5.3 后端打包与配置后端打包标准流程是mvn clean package -DskipTests打成jar包后不依赖外部Tomcat直接java -jar rent-backend-1.0.0.jar有一个部署细节值得注意不同环境的配置应该用SpringBoot的多Profile机制解决。开发环境用application-dev.yml生产环境用application-prod.yml启动时指定java -jar rent-backend.jar --spring.profiles.activeprod生产环境我通常会把数据库密码通过启动参数传入而不是写死在yml文件里。比如java -jar rent-backend.jar --spring.datasource.password你的密码这样做的好处是配置文件和代码一起打包后密码不会泄露在代码仓库中密码修改也只需要重启时换参数。5.4 Vue打包放进SpringBoot的两种部署方案前端打包用npm run build生成dist目录里面的静态文件就是整个前端。部署时有两种主流方案方案一单独部署Vue到Nginx后端jar包单独跑推荐Nginx配置大概长这样server { listen 80; server_name yourdomain.com; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这里try_files $uri $uri/ /index.html;是Vue的history路由模式必须的配置否则刷新页面时路由是/house/detail/1Nginx会去找这个真实路径找不到就404。不加这行页面首次打开没问题F5刷新就白屏。方案二把dist打包进SpringBoot的resources/static目录这种方式适合单机演示场景不用装Nginx一个jar包全搞定。把dist下的所有静态资源复制到src/main/resources/static/重新打包后启动jar包直接访问http://localhost:8080/index.html就能看到前端页面。但这里有一个强制注意事项如果选择方案二Vue路由必须改用hash模式不能使用history模式。因为history模式的路由路径在服务端没有对应的转发规则jar包内部的SpringBoot不会做前端路由的回退处理刷新时必然404。改成hash模式后路径上的#让浏览器只向服务端请求首页路由切换完全由前端控制就不会有404问题。const router new VueRouter({ mode: hash, routes })方案二的另一个麻烦是前端接口请求路径的语境会变化。如果Nginx方案里用了/api做代理转发那方案二里前端axios的baseURL就不能再写/api了应该直接写/或者干脆用相对路径让请求打到SpringBoot本身的接口上。两个方案的本质区别就是谁来处理静态资源和接口转发那层逻辑。6. 常见问题排查与避坑指南6.1 SpringBoot版本太高导致的启动失败启动报错Error creating bean with name xxx或者UnsupportedClassVersionError十有八九是JDK版本和SpringBoot版本不匹配。3.x的SpringBoot必须用JDK17如果项目配置里还有javax.*的老包也需要迁移成jakarta.*。处理这种问题最快的方式是做“版本降级”把SpringBoot降到2.7.x系列而不是硬着头皮改代码适配新版本。另外要检查Maven仓库里有没有缓存冲突。下载过3.x版本依赖后如果不小心把本地的默认SpringBoot版本改掉重新构建时会拉一堆3.x的jar包。我建议在pom.xml的parent里显式写版本号parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.18/version relativePath/ /parent6.2 MyBatis查询条件不生效这个问题的排查路径我总结为三步。第一步确认SQL日志里有执行语句如果没有说明Mapper映射没绑定成功检查XML里的namespace是否和接口全限定名一致。第二步确认参数真的传进来了在test条件里输出参数看是否为空。第三步检查XML里的if条件写法最常见的问题是用了if testkeyword ! null但没判空字符串前端传了空串过来条件不生效导致查询结果异常。代码示例!-- 错误写法空字符串时条件不生效 -- if testkeyword ! null AND title LIKE CONCAT(%, #{keyword}, %) /if !-- 正确写法 -- if testkeyword ! null and keyword ! AND title LIKE CONCAT(%, #{keyword}, %) /if6.3 Vue依赖安装慢与打包失败npm安装依赖慢可以先看是否已经切换了淘宝源。打包失败常见的错误是JavaScript heap out of memory这是Node内存默认限制太小导致的。可以临时加大内存NODE_OPTIONS--max_old_space_size4096 npm run buildWindows下临时指定环境变量还需要set命令更省事的办法是修改package.json里的build脚本scripts: { build: node --max_old_space_size4096 node_modules/vue-cli-service/bin/vue-cli-service.js build }6.4 接口跨域与404问题速查以下是这个项目联调和部署阶段最常见问题的排查速查表现象可能原因解决方案前端调用接口报CORS错误后端没有配置跨域后端增加CorsConfig全局配置或前端用代理方式规避开发环境跨域接口返回404后端接口路径和前端请求路径不一致检查Controller的RequestMapping和前端axios路径建议统一用/api前缀打包后页面白屏资源路径使用了绝对路径/js/xxx.jsVite/Vue CLI里配置publicPath: ./改为相对路径刷新出现404history路由未配置回退Nginx加try_files或改用hash模式上传图片无法访问静态资源映射路径不对检查文件存储路径和访问URL确认没有穿越目录Token过期后跳转循环401错误触发后未清token跳登录页前清除localStorage里的token并带上redirect参数有动手能力的同学可以把这套系统继续往两个方向扩展一是接一个ECharts数据大屏给管理员端展示房源分布、订单走势二是适当引入Redis做热门房源的缓存减轻数据库压力。这两个增强点对面试聊深度的帮助会非常大。我个人在实际操作中的一个体会是做这种前后端分离项目最花时间的往往不是业务功能本身而是环境的“版本编排”和联调阶段的细节对齐。JDK版本、Node版本、MySQL驱动参数、前端路由模式每一个环节出问题都会让人卡很久。所以如果你准备复刻这套系统我的建议是严格按照本文给出的版本组合来搭环境先跑通Hello World级别的接口和页面确认链路通畅后再填充完整业务功能。千万不要一上来就把所有代码写完再统一调环境那样遇到问题定位起来非常困难。把地基打牢后面填业务代码就会顺很多。
返回列表