1. 项目概述
作为一名长期从事企业级应用开发的工程师,我最近完成了一个基于SpringBoot+Vue的房产租赁管理系统。这个项目源于实际需求——传统房产租赁行业普遍存在信息不对称、管理效率低下等问题。通过数字化手段重构租赁流程,我们为房东、租客和管理员搭建了一个高效、透明的在线平台。
系统采用前后端分离架构,后端基于SpringBoot 2.7实现RESTful API,前端使用Vue 3组合式API开发响应式界面。数据库选用MySQL 8.0,通过JWT实现安全的身份认证。特别值得一提的是,我们引入了基于用户行为的协同过滤算法,为租客提供个性化房源推荐。
2. 技术架构设计
2.1 整体架构方案
系统采用经典的B/S三层架构:
- 表现层:Vue.js + Element Plus构建响应式前端
- 业务逻辑层:SpringBoot + MyBatis Plus实现核心业务
- 数据持久层:MySQL 8.0 + Redis缓存
这种分层设计带来了明显的优势:
- 前后端完全解耦,便于独立开发和部署
- 使用Swagger生成API文档,前后端协作更高效
- 采用JWT无状态认证,轻松支持横向扩展
2.2 关键技术选型
在选择技术栈时,我们重点考虑了以下因素:
后端技术:
- SpringBoot 2.7:简化配置,快速启动
- MyBatis Plus:增强的ORM框架,减少样板代码
- Hutool:Java工具包,提高开发效率
- JWT:安全的无状态认证方案
前端技术:
- Vue 3:组合式API更符合开发直觉
- Element Plus:丰富的UI组件库
- Axios:处理HTTP请求
- ECharts:数据可视化展示
提示:在实际开发中,我们通过配置多环境profile(dev/test/prod)实现不同环境的无缝切换,这是企业级应用的标准实践。
3. 核心功能实现
3.1 房源推荐系统
房源推荐是系统的核心价值点。我们实现了基于用户的协同过滤算法:
java复制// 推荐算法核心逻辑
public List<House> recommendHouses(Long userId) {
// 1. 获取用户历史行为数据
List<UserBehavior> behaviors = behaviorMapper.selectByUser(userId);
// 2. 计算相似用户
Map<Long, Double> similarUsers = findSimilarUsers(userId);
// 3. 生成推荐列表
List<House> recommendations = new ArrayList<>();
for (Map.Entry<Long, Double> entry : similarUsers.entrySet()) {
List<House> houses = houseMapper.selectByUser(entry.getKey());
recommendations.addAll(houses);
}
// 4. 过滤已看过的房源并排序
return recommendations.stream()
.filter(h -> !behaviors.contains(h.getId()))
.sorted(Comparator.comparingDouble(House::getScore).reversed())
.limit(10)
.collect(Collectors.toList());
}
3.2 权限控制系统
系统涉及三类角色,我们通过Spring Security实现细粒度权限控制:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/landlord/**").hasRole("LANDLORD")
.antMatchers("/api/user/**").hasRole("USER")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()));
}
}
4. 数据库设计
4.1 主要实体关系
系统核心实体包括:
- 用户(User):存储所有用户基础信息
- 房东(Landlord):扩展用户信息,关联房源
- 房源(House):包含房源详细属性
- 订单(Order):记录租赁交易
- 合同(Contract):电子合同存储
4.2 关键表结构示例
sql复制CREATE TABLE `house` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL COMMENT '房源标题',
`address` varchar(200) NOT NULL COMMENT '详细地址',
`price` decimal(10,2) NOT NULL COMMENT '月租金',
`area` decimal(6,2) NOT NULL COMMENT '面积(m²)',
`room_type` varchar(20) NOT NULL COMMENT '户型',
`landlord_id` bigint NOT NULL COMMENT '房东ID',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(1可租/0已租)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_landlord` (`landlord_id`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='房源信息表';
5. 系统实现细节
5.1 文件上传服务
文件上传是房源管理的核心功能之一。我们实现了通用的文件上传接口:
java复制@RestController
@RequestMapping("/api/file")
public class FileController {
@Value("${file.upload-dir}")
private String uploadDir;
@PostMapping("/upload")
public Result<String> uploadFile(@RequestParam("file") MultipartFile file) {
try {
// 确保上传目录存在
File dir = new File(uploadDir);
if (!dir.exists()) {
dir.mkdirs();
}
// 生成唯一文件名
String filename = UUID.randomUUID() +
file.getOriginalFilename().substring(
file.getOriginalFilename().lastIndexOf(".")
);
// 保存文件
Path path = Paths.get(uploadDir, filename);
Files.copy(file.getInputStream(), path,
StandardCopyOption.REPLACE_EXISTING);
return Result.success(filename);
} catch (Exception e) {
log.error("文件上传失败", e);
return Result.error("上传失败");
}
}
}
5.2 预约看房流程
预约看房功能涉及复杂的业务状态管理:
- 租客提交预约申请
- 房东确认/拒绝预约
- 双方完成看房后确认
- 生成租赁意向或取消预约
我们使用状态模式优雅地处理这一流程:
java复制public interface AppointmentState {
void confirm(AppointmentContext context);
void cancel(AppointmentContext context);
void complete(AppointmentContext context);
}
@Component
@Scope("prototype")
public class PendingState implements AppointmentState {
@Override
public void confirm(AppointmentContext context) {
context.setState(new ConfirmedState());
// 发送通知给租客
notificationService.send(context.getTenantId(),
"您的预约已确认");
}
// 其他方法实现...
}
6. 性能优化实践
6.1 缓存策略
为提高系统响应速度,我们实施了多级缓存:
- 本地缓存:使用Caffeine缓存热点数据
java复制@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES)
.maximumSize(1000));
return cacheManager;
}
- 分布式缓存:Redis缓存共享数据
java复制@Cacheable(value = "houses", key = "#id")
public House getHouseById(Long id) {
return houseMapper.selectById(id);
}
6.2 数据库优化
针对高频查询进行了以下优化:
- 为所有外键字段添加索引
- 对大文本字段使用垂直分表
- 对房源表按城市进行水平分片
- 使用EXPLAIN分析慢查询
7. 安全防护措施
7.1 数据安全
- 敏感字段加密:用户密码、身份证号等使用AES加密存储
java复制public class CryptoUtils {
private static final String KEY = "secure-key-12345";
public static String encrypt(String data) {
// AES加密实现...
}
}
- SQL注入防护:全程使用MyBatis Plus参数化查询
- XSS防护:前端使用DOMPurify净化输入
7.2 接口安全
- JWT令牌过期时间设置为2小时
- 敏感操作需要二次验证
- 接口限流:使用Guava RateLimiter防止暴力请求
8. 部署实施方案
8.1 容器化部署
我们使用Docker Compose编排服务:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
8.2 CI/CD流程
项目配置了完整的GitLab CI流水线:
- 代码提交触发构建
- 运行单元测试和集成测试
- 构建Docker镜像并推送到仓库
- 自动部署到测试环境
- 人工确认后发布生产环境
9. 踩坑经验分享
在实际开发中,我们遇到了几个典型问题:
- Vue响应式丢失问题:
- 现象:直接通过索引修改数组元素时,视图不更新
- 解决方案:使用Vue.set或展开运算符创建新数组
javascript复制// 错误做法
this.items[index] = newValue;
// 正确做法
this.$set(this.items, index, newValue);
// 或
this.items = [...this.items.slice(0, index), newValue, ...this.items.slice(index+1)]
- MyBatis Plus逻辑删除陷阱:
- 现象:误以为逻辑删除的数据会自动过滤
- 解决方案:需要手动配置@TableLogic字段
java复制@TableLogic
private Integer deleted;
- JWT令牌刷新问题:
- 现象:令牌过期后用户体验差
- 解决方案:实现无感刷新机制
javascript复制// 在axios响应拦截器中处理token刷新
axios.interceptors.response.use(response => {
return response;
}, error => {
if (error.response.status === 401) {
return refreshToken().then(() => {
return axios(error.config);
});
}
return Promise.reject(error);
});
这个项目从技术选型到最终上线历时3个月,期间我们不断调整架构设计,优化用户体验。最大的收获是认识到:一个好的系统不仅需要强大的技术支撑,更需要深入理解业务场景,在技术先进性和开发效率之间找到平衡点。