智慧租房管理系统
项目简介一个基于 Vue 3 Spring Boot 的在线租房管理平台支持租户、房东、管理员三种角色集成 DeepSeek AI 实现智能房源推荐。目录项目流程总览用户认证体系角色权限控制数据层设计统一响应与错误处理DeepSeek AI 智能推荐前后端通信前端架构技术栈清单功能清单1. 项目流程总览用户注册/登录 → 角色路由分发 → 各端功能页面 │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ 租客端 房东端 管理端 - 浏览房源 - 发布房源 - 用户管理 - 下单租房 - 订单管理 - 房源管理 - 租房攻略 - 数据概览 - 订单管理 │ ▼ DeepSeek AI 智能推荐 根据偏好匹配房源整体架构├── 后端 (backend/) ← Spring Boot MyBatis-Plus MySQL │ ├── controller/ ← RESTful API18 个端点 │ ├── service/ ← 业务逻辑 DeepSeek AI 推荐 │ ├── entity/ ← 4 个实体User/House/Order/VerificationCode │ ├── mapper/ ← MyBatis-Plus 数据访问层 │ ├── dto/ ← 推荐请求/响应 DTO │ ├── config/ ← CORS MyBatis-Plus 分页插件 │ └── util/ ← MD5 加盐加密工具 ├── 前端 (frontend/) ← Vue 3 Vite Element Plus │ ├── api/ ← axios 封装 3 个 API 模块 │ ├── router/ ← 路由表 导航守卫RBAC │ └── views/ ← 按角色分目录tenant / landlord / admin ├── 初始化数据库.py ← 删库重建 导入 SQL ├── 启动前端.py / 启动后端.py ← 一键启动脚本 └── README.md2. 用户认证体系注册流程用户填写注册表单 → 输入邮箱 → 点击获取验证码 │ ├── 前端校验邮箱格式 → 60 秒倒计时防重复点击 ├── POST /api/user/send-code │ ├── 后端 60 秒频控检查同一邮箱不可重复发送 │ ├── 生成 6 位随机验证码 → 写入 verification_code 表 │ └── Spring Mail 发送邮件 │ ├── 用户输入验证码 密码 → POST /api/user/register │ ├── 校验用户名唯一性 │ ├── 校验验证码邮箱 验证码 REGISTER类型 未使用 未过期 │ ├── 标记验证码为已使用 │ └── MD5 加盐加密密码 → 写入 user 表 │ └── 注册成功 → 自动跳转登录页登录流程POST /api/user/login { username, password } │ ├── 密码 MD5 加盐加密 → 与数据库密文比对 ├── 校验用户状态 enabled 1停用账户不可登录 ├── 返回用户 JSON密码字段清空 │ └── 前端localStorage.setItem(user, JSON.stringify(user)) → router.push 跳转到角色对应首页密码存储MD5 加盐// MD5Util.javaprivatestaticfinalStringSALTsmartrental2026;publicstaticStringmd5(StringplainText){MessageDigestmdMessageDigest.getInstance(MD5);byte[]digestmd.digest((plainTextSALT).getBytes());// 转 32 位小写十六进制StringBuildersbnewStringBuilder();for(byteb:digest){sb.append(String.format(%02x,b0xff));}returnsb.toString();}邮箱验证码设计verification_code 表 ├── email-- 目标邮箱├── code-- 6 位数字验证码├──type-- REGISTER可扩展RESET_PASSWORD 等├── expires_at-- 5 分钟过期├── used-- 0未使用, 1已使用防重复利用└── create_time-- 用于 60 秒频控判断防刷机制60 秒内同一邮箱不可重复发送createTime 60s now则拒绝验证码 5 分钟过期expiresAt now则拒绝验证码使用后标记used 1不可二次使用3. 角色权限控制三角色 RBAC 模型┌───────────────┐ │ 未登录用户 │ │ 只能访问 /login │ └───────┬───────┘ │ 登录 ┌────────────┼────────────┐ ▼ ▼ ▼ TENANT LANDLORD ADMIN 租客端 房东端 管理端 /tenant/* /landlord/* /admin/*前端路由守卫// router/index.js — 全局前置导航守卫router.beforeEach((to,from,next){// 1. 登录页 → 放行if(to.path/login){next();return}// 2. 未登录 → 强制跳转登录页constuserJSON.parse(localStorage.getItem(user)||null)if(!user){next(/login);return}// 3. 角色不匹配 → 重定向到角色对应首页if(to.meta.roleto.meta.role!user.role){constroleMap{TENANT:/tenant,LANDLORD:/landlord,ADMIN:/admin}next(roleMap[user.role]||/login);return}// 4. 全部通过 → 放行next()})路由懒加载// 所有页面组件都使用动态 importVite 自动代码分割{path:dashboard,component:()import(../views/tenant/TenantDashboard.vue)}效果用户只加载当前角色的页面代码租客不会下载房东/管理端的 JS。4. 数据层设计4 张核心表user-- 用户表 (username, password, role, email, enabled, ...)house-- 房源表 (title, price, area, type, status, landlord_id, ...)rental_order-- 订单表 (house_id, tenant_id, landlord_id, start_date, end_date, total_amount, status, ...)verification_code-- 验证码表 (email, code, type, expires_at, used, ...)MyBatis-Plus极简数据访问层MyBatis-Plus 的核心价值——零 SQL 实现标准 CRUD// UserServiceImpl — 继承 ServiceImpl 后自动获得// save(entity) — 插入// updateById(entity) — 更新// removeById(id) — 逻辑删除// getById(id) — 按主键查询// page(page, wrapper) — 分页查询// list(wrapper) — 条件列表查询ServicepublicclassUserServiceImplextendsServiceImplUserMapper,UserimplementsUserService{// 只写自定义业务逻辑登录、注册、发验证码// 标准 CRUD 全部继承自 ServiceImpl一行代码都不用写}HouseService 和 RentalOrderService 更是极端案例——完全使用默认 CRUD类体只有两行ServicepublicclassHouseServiceImplextendsServiceImplHouseMapper,HouseimplementsHouseService{// 空类体MyBatis-Plus 提供的默认实现已满足所有需求}分页查询模式// 典型的分页接口模式Lambda 条件 Page 对象 → 自动生成 LIMIT COUNTGetMapping(/page)publicResultPageHousepage(RequestParam(defaultValue1)Integercurrent,RequestParam(defaultValue10)Integersize,RequestParam(requiredfalse)Stringtitle,RequestParam(requiredfalse)Stringstatus){LambdaQueryWrapperHousewrappernewLambdaQueryWrapper();if(title!null!title.isEmpty())wrapper.like(House::getTitle,title);if(status!null!status.isEmpty())wrapper.eq(House::getStatus,status);wrapper.orderByDesc(House::getCreateTime);returnResult.success(houseService.page(newPage(current,size),wrapper));}Lambda 条件构造器的好处字段名用House::getTitle而不是字符串title重构时 IDE 自动改名杜绝字符串拼写错误。逻辑删除TableLogicprivateIntegerdeleted;// 0未删除, 1已删除所有removeById()调用自动转换为UPDATE SET deleted1永不物理删除数据。查询自动追加WHERE deleted0。自动填充TableField(fillFieldFill.INSERT)// 插入时自动填当前时间privateLocalDateTimecreateTime;TableField(fillFieldFill.INSERT_UPDATE)// 插入和更新时自动填privateLocalDateTimeupdateTime;免去每次手动setCreateTime(LocalDateTime.now())。5. 统一响应与错误处理Result 泛型封装// com.smartrental.common.ResultTpublicclassResultT{privateIntegercode;// 200成功, 500失败privateStringmessage;// 提示信息privateTdata;// 泛型数据// 静态工厂方法调用方代码极简洁publicstaticTResultTsuccess(Tdata)→Result(200,操作成功,data)publicstaticTResultTerror(Stringmsg)→Result(500,msg,null)}使用效果// Controller 层——每个方法都返回 Result格式完全统一PostMapping(/login)publicResultUserlogin(...){UseruseruserService.login(username,password);if(user!null)returnResult.success(登录成功,user);returnResult.error(用户名或密码错误);}前端收到的 JSON 格式永远一致{code:200,message:登录成功,data:{id:1,username:zhangsan,role:TENANT}}错误处理策略// Service 层抛 RuntimeException → Controller 层 try/catch → Result.error(e.getMessage())try{UserregistereduserService.register(user,code);returnResult.success(注册成功,registered);}catch(RuntimeExceptione){returnResult.error(e.getMessage());// 用户名已存在 / 验证码错误 / 验证码已过期}没有全局异常处理器——每个 Controller 方法自己做 try/catch。对于教学项目来说足够清晰生产环境建议用ControllerAdvice统一处理。6. DeepSeek AI 智能推荐为什么集成 AI传统房源筛选只能按价格/面积/户型做硬匹配但用户的真实需求往往是模糊的——“想找一个安静、交通方便、适合养宠物的房子”。DeepSeek 大模型可以理解这种自然语言需求从可租房源中智能匹配。推荐流程POST /api/house/recommend { budget, area, roomType, location, description } │ ├── 1. 查询所有状态为 AVAILABLE可租的房源 ├── 2. 构造 Prompt │ ├── System: 你是专业的租房推荐助手按 JSON 格式返回 │ └── User: 用户偏好 房源列表序号、标题、户型、面积、价格、地址、描述 ├── 3. 调用 DeepSeek Chat APIHTTP POST, Bearer Token 认证 │ └── temperature0.3低温度输出更确定 ├── 4. 解析 AI 返回的 JSON → ListRecommendResult │ └── 每个结果房源对象 匹配度评分(0-100) 推荐理由(2-4条) └── 5. 失败降级API 异常时自动切换为规则匹配Prompt 设计System Prompt: 你是一个专业的租房推荐助手。根据用户的偏好和文字描述从房源列表中推荐最合适的房源。 请严格按以下JSON格式返回不要输出其他内容 {results:[{houseIndex:1,score:85,reasons:[预算匹配,交通便利]}]} houseIndex是房源列表中的序号(从1开始)score是0-100的匹配度评分reasons是2-4个推荐理由(用中文)。 只返回得分30的房源按score降序排列。 User Prompt: 用户偏好预算 2000-3000元/月、面积 50-80㎡、位置 朝阳区 用户描述希望房子采光好周边有地铁站 可租房源列表 [1] 阳光花园温馨一居 | 一室一厅 | 55㎡ | ¥2500/月 | 朝阳区望京 | 精装修... [2] ...降级方案// DeepSeekService.java: fallbackRecommend()privateListRecommendResultfallbackRecommend(RecommendRequestreq,ListHousehouses){for(Househ:houses){intscore10;// 基础分// 位置匹配地址包含用户位置关键词 → 30if(h.getAddress().contains(req.getLocation()))score30;// 户型匹配类型包含用户户型偏好 → 30if(h.getType().contains(req.getRoomType()))score30;// 有描述需求 → 5if(req.getDescription()!null)score5;if(score10)results.add(newRecommendResult(h,Math.min(score,70),reasons));}}降级保证即使 DeepSeek API 挂了系统仍然能给出基于规则的推荐结果。7. 前后端通信Vite 开发代理// vite.config.js — 开发环境代理配置server:{port:3000,proxy:{/api:{target:http://localhost:8080,// 后端地址changeOrigin:true,}}}效果浏览器请求 → http://localhost:3000/api/user/login ↓ Vite 代理转发 http://localhost:8080/api/user/login开发环境下前后端同域不存在跨域问题。生产环境用 CORS 配置兜底。axios 封装// api/index.jsconstapiaxios.create({baseURL:/api,// 所有请求以 /api 开头timeout:10000,// 10 秒超时})// 响应拦截器自动解包 response.dataapi.interceptors.response.use(rr.data,err{...})调用方代码极简constresawaituserApi.login({username,password})// res 已经是 { code: 200, message: 登录成功, data: {...} }// 不需要再写 res.data.code拦截器已经解包了一层CORS 配置// CorsConfig.java — 允许任意来源跨域请求config.addAllowedOriginPattern(*);config.addAllowedHeader(*);config.addAllowedMethod(*);config.setAllowCredentials(true);开发环境宽松配置生产环境应限制具体域名。8. 前端架构路由结构/login → Login.vue 公开页面 /tenant → TenantLayout.vue 租客端布局 /tenant/dashboard → 首页 /tenant/houses → 房源列表 /tenant/orders → 我的订单 /tenant/guidance → 租房攻略 /landlord → LandlordLayout.vue房东端布局 /landlord/dashboard → 首页 /landlord/houses → 我的房源 /landlord/orders → 订单管理 /admin → AdminLayout.vue 管理端布局 /admin/dashboard → 首页 /admin/users → 用户管理 /admin/houses → 房源管理 /admin/orders → 订单管理设计模式Layout 子路由每个角色有一个 Layout 组件提供侧边栏/顶栏壳子router-view嵌套子路由TenantLayout.vue ├── 侧边导航栏仪表盘 / 浏览房源 / 我的订单 / 租房攻略 └── router-view / ← 子路由页面渲染在这里好处切换子页面时只会替换内容区侧边栏保持不动避免页面整体刷新。登录状态管理没有用 Vuex/Pinia——直接用localStorage// 登录写入localStorage.setItem(user,JSON.stringify(res.data))// 读取路由守卫和各个页面都从 localStorage 读constuserJSON.parse(localStorage.getItem(user)||null)9. 技术栈清单模块技术选型理由后端框架Spring Boot 3.4.3Java 生态最主流自动配置 内嵌 TomcatORMMyBatis-Plus 3.5.10零 SQL 基础 CRUDLambda 条件构造器分页插件数据库MySQL 8.0关系型数据用户/房源/订单天然适合关系模型前端框架Vue 3 (Composition API)响应式 组件化script setup写法简洁构建工具Vite 8开发秒级热更新比 Webpack 快一个数量级UI 组件库Element PlusVue 3 生态最成熟的后台 UI 库路由Vue Router 4嵌套路由 导航守卫天然支持 Layout 模式HTTP 客户端Axios拦截器机制请求/响应统一处理AI 推荐DeepSeek Chat API成本极低中文能力强API 兼容 OpenAI 格式邮件服务Spring Mail (JavaMailSender)Spring Boot 自动配置几行代码发邮件10. 功能清单用户系统用户注册用户登录租客端浏览房源列表AI 智能推荐下单租房查看我的订单房东端房源管理订单管理管理端用户管理房源管理订单管理如果对你有帮助可以给个 Star ⭐欢迎提 Issue 讨论。GitHubhttps://github.com/LuckLuffy/smart-rental