项目简介

一个基于 Vue 3 + Spring Boot 的在线租房管理平台,支持租户、房东、管理员三种角色,集成 DeepSeek AI 实现智能房源推荐。


目录

  1. 项目流程总览
  2. 用户认证体系
  3. 角色权限控制
  4. 数据层设计
  5. 统一响应与错误处理
  6. DeepSeek AI 智能推荐
  7. 前后端通信
  8. 前端架构
  9. 技术栈清单
  10. 功能清单

1. 项目流程总览

用户注册/登录 → 角色路由分发 → 各端功能页面
                    │
    ┌───────────────┼───────────────┐
    ▼               ▼               ▼
  租客端          房东端          管理端
  - 浏览房源      - 发布房源      - 用户管理
  - 下单租房      - 订单管理      - 房源管理
  - 租房攻略      - 数据概览      - 订单管理
                    │
                    ▼
         DeepSeek AI 智能推荐
         (根据偏好匹配房源)

整体架构

├── 后端 (backend/)          ← Spring Boot + MyBatis-Plus + MySQL
│   ├── controller/          ← RESTful API,18 个端点
│   ├── 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.md

2. 用户认证体系

注册流程

用户填写注册表单 → 输入邮箱 → 点击"获取验证码"
    │
    ├── 前端校验:邮箱格式 → 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.java
private static final String SALT = "smartrental2026";

public static String md5(String plainText) {
    MessageDigest md = MessageDigest.getInstance("MD5");
    byte[] digest = md.digest((plainText + SALT).getBytes());
    // 转 32 位小写十六进制
    StringBuilder sb = new StringBuilder();
    for (byte b : digest) {
        sb.append(String.format("%02x", b & 0xff));
    }
    return sb.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. 未登录 → 强制跳转登录页
  const user = JSON.parse(localStorage.getItem('user') || 'null')
  if (!user) { next('/login'); return }

  // 3. 角色不匹配 → 重定向到角色对应首页
  if (to.meta.role && to.meta.role !== user.role) {
    const roleMap = { TENANT: '/tenant', LANDLORD: '/landlord', ADMIN: '/admin' }
    next(roleMap[user.role] || '/login'); return
  }

  // 4. 全部通过 → 放行
  next()
})

路由懒加载

// 所有页面组件都使用动态 import,Vite 自动代码分割
{ 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)   — 条件列表查询

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    // 只写自定义业务逻辑(登录、注册、发验证码)
    // 标准 CRUD 全部继承自 ServiceImpl,一行代码都不用写
}

HouseService 和 RentalOrderService 更是极端案例——完全使用默认 CRUD,类体只有两行:

@Service
public class HouseServiceImpl extends ServiceImpl<HouseMapper, House> implements HouseService {
    // 空类体,MyBatis-Plus 提供的默认实现已满足所有需求
}

分页查询模式

// 典型的分页接口模式:Lambda 条件 + Page 对象 → 自动生成 LIMIT + COUNT
@GetMapping("/page")
public Result<Page<House>> page(
        @RequestParam(defaultValue = "1") Integer current,
        @RequestParam(defaultValue = "10") Integer size,
        @RequestParam(required = false) String title,
        @RequestParam(required = false) String status) {
    LambdaQueryWrapper<House> wrapper = new LambdaQueryWrapper<>();
    if (title != null && !title.isEmpty()) wrapper.like(House::getTitle, title);
    if (status != null && !status.isEmpty()) wrapper.eq(House::getStatus, status);
    wrapper.orderByDesc(House::getCreateTime);
    return Result.success(houseService.page(new Page<>(current, size), wrapper));
}

Lambda 条件构造器的好处:字段名用 House::getTitle 而不是字符串 "title",重构时 IDE 自动改名,杜绝字符串拼写错误。

逻辑删除

@TableLogic
private Integer deleted;  // 0=未删除, 1=已删除

所有 removeById() 调用自动转换为 UPDATE SET deleted=1,永不物理删除数据。查询自动追加 WHERE deleted=0

自动填充

@TableField(fill = FieldFill.INSERT)         // 插入时自动填当前时间
private LocalDateTime createTime;

@TableField(fill = FieldFill.INSERT_UPDATE)  // 插入和更新时自动填
private LocalDateTime updateTime;

免去每次手动 setCreateTime(LocalDateTime.now())


5. 统一响应与错误处理

Result 泛型封装

// com.smartrental.common.Result<T>
public class Result<T> {
    private Integer code;    // 200=成功, 500=失败
    private String message;  // 提示信息
    private T data;          // 泛型数据

    // 静态工厂方法,调用方代码极简洁
    public static <T> Result<T> success(T data)Result(200, "操作成功", data)
    public static <T> Result<T> error(String msg)Result(500, msg, null)
}

使用效果

// Controller 层——每个方法都返回 Result,格式完全统一
@PostMapping("/login")
public Result<User> login(...) {
    User user = userService.login(username, password);
    if (user != null) return Result.success("登录成功", user);
    return Result.error("用户名或密码错误");
}

前端收到的 JSON 格式永远一致:

{ "code": 200, "message": "登录成功", "data": { "id": 1, "username": "zhangsan", "role": "TENANT" } }

错误处理策略

// Service 层抛 RuntimeException → Controller 层 try/catch → Result.error(e.getMessage())
try {
    User registered = userService.register(user, code);
    return Result.success("注册成功", registered);
} catch (RuntimeException e) {
    return Result.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 API(HTTP POST, Bearer Token 认证)
    │     └── temperature=0.3(低温度,输出更确定)
    ├── 4. 解析 AI 返回的 JSON → List<RecommendResult>
    │     └── 每个结果:房源对象 + 匹配度评分(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()
private List<RecommendResult> fallbackRecommend(RecommendRequest req, List<House> houses) {
    for (House h : houses) {
        int score = 10;  // 基础分
        // 位置匹配:地址包含用户位置关键词 → +30
        if (h.getAddress().contains(req.getLocation())) score += 30;
        // 户型匹配:类型包含用户户型偏好 → +30
        if (h.getType().contains(req.getRoomType())) score += 30;
        // 有描述需求 → +5
        if (req.getDescription() != null) score += 5;
        if (score > 10) results.add(new RecommendResult(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.js
const api = axios.create({
  baseURL: '/api',       // 所有请求以 /api 开头
  timeout: 10000,        // 10 秒超时
})

// 响应拦截器:自动解包 response.data
api.interceptors.response.use(r => r.data, err => { ... })

调用方代码极简:

const res = await userApi.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 读
const user = JSON.parse(localStorage.getItem('user') || 'null')


9. 技术栈清单

模块 技术 选型理由
后端框架 Spring Boot 3.4.3 Java 生态最主流,自动配置 + 内嵌 Tomcat
ORM MyBatis-Plus 3.5.10 零 SQL 基础 CRUD,Lambda 条件构造器,分页插件
数据库 MySQL 8.0 关系型数据(用户/房源/订单天然适合关系模型)
前端框架 Vue 3 (Composition API) 响应式 + 组件化,<script setup> 写法简洁
构建工具 Vite 8 开发秒级热更新,比 Webpack 快一个数量级
UI 组件库 Element Plus Vue 3 生态最成熟的后台 UI 库
路由 Vue Router 4 嵌套路由 + 导航守卫,天然支持 Layout 模式
HTTP 客户端 Axios 拦截器机制,请求/响应统一处理
AI 推荐 DeepSeek Chat API 成本极低,中文能力强,API 兼容 OpenAI 格式
邮件服务 Spring Mail (JavaMailSender) Spring Boot 自动配置,几行代码发邮件


10. 功能清单

用户系统

  • 用户注册
  • 用户登录

租客端

  • 浏览房源列表
  • AI 智能推荐
  • 下单租房
  • 查看我的订单

房东端

  • 房源管理
  • 订单管理

管理端

  • 用户管理
  • 房源管理
  • 订单管理

如果对你有帮助可以给个 Star ⭐,欢迎提 Issue 讨论。

GitHub:https://github.com/LuckLuffy/smart-rental

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐