news 2026/10/7 18:04:39

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于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对象的映射相比JPA,SQL是显式可控的,像房屋搜索这种复杂查询和排序能精细优化
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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 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`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 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不写SQL,Mapper只做数据访问。很多毕设代码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 id="pageHouse" resultType="com.example.rent.entity.House"> SELECT * FROM house <where> <if test="keyword != null and keyword != ''"> AND (title LIKE CONCAT('%', #{keyword}, '%') OR address LIKE CONCAT('%', #{keyword}, '%')) </if> <if test="city != null and city != ''"> AND city = #{city} </if> <if test="minPrice != null"> AND price <![CDATA[ >= ]]> #{minPrice} </if> <if test="maxPrice != null"> AND price <![CDATA[ <= ]]> #{maxPrice} </if> <if test="status != null"> AND status = #{status} </if> </where> ORDER BY <choose> <when test="sort == 'price_asc'">price ASC</when> <when test="sort == 'price_desc'">price DESC</when> <when test="sort == 'time_desc'">create_time DESC</when> <otherwise>create_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机制天然解决了这个问题。

实现逻辑三步走:

  1. 用户登录成功后,后端生成一个Token,里面封装用户ID、用户名、角色,用HMAC密钥签名,设置有效期(我设置了24小时)。
  2. 前端拿到Token后存到localStorage,每次axios请求在拦截器里自动加上Authorization: Bearer xxx请求头。
  3. 后端写一个拦截器,放行登录、注册、房源列表公开接口,其余接口统一解析Token,解析失败直接返回401。

唯一要提醒的是JWT最大的弱点是无法注销,用户改密码或者被管理员封号后,已签发的Token在有效期内依然能用。我项目里通过用户表的status字段做了二次校验:每次请求拦截器解析出用户ID后,查一下账号是否被禁用,禁用就拒绝访问。这个思路在面试中很加分,体现出你对安全的深入理解。

权限控制我是通过自定义注解@RequireRole实现的,在需要房东权限的接口上加@RequireRole("landlord"),拦截器里解析注解并根据当前登录用户的角色决定是否放行。这样比在方法内部写死角色判断要优雅得多。

3.4 MyBatis开发中的几个高频坑

第一个坑是实体类字段与数据库字段映射不上。数据库字段用下划线命名(create_time),Java字段用驼峰命名(createTime),MyBatis默认不能自动映射,结果查出来的字段全是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.js

4.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.8(8u201+)稳定可靠,生态兼容性最好
Maven3.6.3不要用太新的3.9.x配旧项目可能有兼容问题
SpringBoot2.7.18最后支持JDK8的版本线
Node.js18.x LTS兼容Vue CLI 5
MySQL8.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?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: yourpassword driver-class-name: com.mysql.cj.jdbc.Driver

其中allowPublicKeyRetrieval=true是MySQL 8的典型坑。如果缺少这个参数,连接时会报Public Key Retrieval is not allowed,这是MySQL 8默认的caching_sha2_password认证方式导致的,用了mysql_native_password或sha256_password以外的插件,就必须配置这个参数。

字符集方面,数据库连接串里characterEncoding=utf8是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.active=prod

生产环境我通常会把数据库密码通过启动参数传入,而不是写死在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/1,Nginx会去找这个真实路径,找不到就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> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.18</version> <relativePath/> </parent>

6.2 MyBatis查询条件不生效

这个问题的排查路径我总结为三步。第一步,确认SQL日志里有执行语句,如果没有说明Mapper映射没绑定成功,检查XML里的namespace是否和接口全限定名一致。第二步,确认参数真的传进来了,在test条件里输出参数看是否为空。第三步,检查XML里的<if>条件写法,最常见的问题是用了<if test="keyword != null">但没判空字符串,前端传了空串过来,条件不生效导致查询结果异常。

代码示例:

<!-- 错误写法:空字符串时条件不生效 --> <if test="keyword != null"> AND title LIKE CONCAT('%', #{keyword}, '%') </if> <!-- 正确写法 --> <if test="keyword != null and keyword != ''"> AND title LIKE CONCAT('%', #{keyword}, '%') </if>

6.3 Vue依赖安装慢与打包失败

npm安装依赖慢,可以先看是否已经切换了淘宝源。打包失败常见的错误是JavaScript heap out of memory,这是Node内存默认限制太小导致的。可以临时加大内存:

NODE_OPTIONS=--max_old_space_size=4096 npm run build

Windows下临时指定环境变量还需要set命令,更省事的办法是修改package.json里的build脚本:

"scripts": { "build": "node --max_old_space_size=4096 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级别的接口和页面,确认链路通畅后再填充完整业务功能。千万不要一上来就把所有代码写完再统一调环境,那样遇到问题定位起来非常困难。把地基打牢,后面填业务代码就会顺很多。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 18:04:32

eFuse与MCU协作的嵌入式电源路径保护设计实战

1. 项目概述与电源路径保护的核心价值1.1 为什么嵌入式和工业应用需要电源路径保护做嵌入式有一段时间的朋友应该都有过这种经历&#xff1a;设备第一次上电&#xff0c;或者在现场接错了一根线&#xff0c;板子上“啪”的一声冒烟&#xff0c;一个 BUCK 芯片、一颗电容或者一块…

作者头像 李华
网站建设 2026/10/7 18:03:54

Logisim手把手搭建4位行波进位加法器与补码加减法

如果你正在学计算机组成原理或者数字逻辑&#xff0c;大概率绕不开 Logisim 这个东西。我当年第一次打开 Logisim&#xff0c;是想在搭 MIPS 单周期 CPU 之前&#xff0c;把最底层的算术单元搞明白&#xff1b;结果一上手就发现&#xff0c;看着简单的一个加法器&#xff0c;真…

作者头像 李华
网站建设 2026/10/7 18:02:40

知乎看山智能体MCP工具实战:让用户建议驱动AI自我进化

1. 这不是“加个按钮”那么简单&#xff1a;给知乎看山智能体配一个真正能落地的MCP工具你有没有试过&#xff0c;在知乎看山智能体里问完一个问题&#xff0c;刚想补充一句“这个回答如果能加上2023年后的数据就更准了”&#xff0c;结果发现——没地方写&#xff1f;或者点开…

作者头像 李华
网站建设 2026/10/7 18:02:25

SPICE模型深度解析:从参数提取到LTspice仿真实战

很多刚接触电路仿真的朋友都有个错觉&#xff1a;觉得SPICE模型就是一堆看不懂的数学公式&#xff0c;或者认为仿真结果不对就一定是软件算错了。其实我干了十几年电路设计&#xff0c;最大的体会是——SPICE模型反而是一个比较容易被忽视、却对仿真结果影响极大的环节。不管是…

作者头像 李华
网站建设 2026/10/7 18:02:25

从零搭建 t3code:Next.js + tRPC + Prisma 的全栈类型安全实践

这些年我一直在TypeScript全栈这条路上折腾&#xff0c;从最早的传统前后端分离&#xff0c;到后来用GraphQL&#xff0c;再到整个工具链打通类型系统&#xff0c;说实话每一步都有收获&#xff0c;但每到一个新项目就得重新配一轮脚手架、理一轮类型定义&#xff0c;重复劳动特…

作者头像 李华
网站建设 2026/10/7 18:01:33

继续教育论文降AI率实战:从检测原理到8款工具测评与改写工作流

上个月一个朋友急得不行&#xff0c;说她在成人本科读工商管理&#xff0c;论文初稿传上去&#xff0c;学校系统直接标了“疑似AI生成&#xff1a;76%”&#xff0c;再不处理可能要延期。这种事在2026年早就不是个例了。很多继续教育院校、开放大学、在职专升本项目的论文系统都…

作者头像 李华