1. 项目背景与核心价值
房屋租赁管理系统是当前房地产数字化浪潮中的典型应用。随着城市化进程加速和人口流动频繁,传统的线下租房模式暴露出信息不对称、交易效率低下、合同管理混乱等问题。这个基于SpringBoot的智慧住房租赁平台,本质上是通过技术手段重构租赁业务流程,实现从房源展示、在线签约到租后管理的全链路数字化。
我在实际开发这类系统时发现,真正有价值的租赁平台需要同时解决三个核心痛点:一是消除房东与租客之间的信息差,二是降低交易双方的信任成本,三是通过标准化流程减少纠纷。这个系统采用SpringBoot+Thymeleaf的技术组合,既保证了后台服务的稳定性,又实现了轻量级的前端交互,特别适合中小型房产中介或自主房东使用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计解析
2.1 SpringBoot框架选型考量
选择SpringBoot作为基础框架主要基于四个实际考量:
- 快速迭代:毕业设计周期有限,SpringBoot的starter依赖和自动配置能节省大量环境搭建时间
- 内嵌容器:无需额外配置Tomcat,通过main方法即可启动服务,方便演示和部署
- 生态完整:与MyBatis、Redis等常用组件无缝集成,扩展性强
- 监控完善:Actuator端点提供健康检查、性能监控等功能
典型依赖配置示例:
xml复制<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
</dependencies>
2.2 Thymeleaf模板引擎优势
相比JSP和FreeMarker,Thymeleaf在租赁系统中的独特优势:
- 自然模板:HTML文件可直接在浏览器打开预览,开发调试方便
- 强类型检查:避免模板中常见的空指针异常
- 表达式语法:支持OGNL表达式,方便处理房源数据展示逻辑
- 片段复用:通过th:fragment实现页面组件化,如统一的房源卡片样式
实际开发中发现:Thymeleaf的缓存机制在开发阶段需要关闭,否则修改模板后需要重启服务
配置示例:spring.thymeleaf.cache=false
3. 核心功能模块实现
3.1 房源信息管理模块
3.1.1 数据结构设计
房源表(house)关键字段设计:
sql复制CREATE TABLE `house` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '房源ID',
`title` varchar(100) NOT NULL COMMENT '房源标题',
`address` varchar(255) NOT NULL COMMENT '详细地址',
`price` decimal(10,2) NOT NULL COMMENT '月租金',
`area` decimal(6,2) NOT NULL COMMENT '面积(㎡)',
`room_type` tinyint(4) NOT NULL COMMENT '户型(1-一居 2-两居...)',
`facilities` varchar(255) DEFAULT NULL COMMENT '设施(空调,洗衣机等)',
`status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态(0-待租 1-已租)',
`landlord_id` bigint(20) NOT NULL COMMENT '房东ID',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_status` (`status`),
KEY `idx_landlord` (`landlord_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.1.2 多条件搜索实现
采用动态SQL构建查询条件:
java复制@Mapper
public interface HouseMapper {
@Select("<script>" +
"SELECT * FROM house WHERE status=0 " +
"<if test='minPrice != null'> AND price >= #{minPrice}</if>" +
"<if test='maxPrice != null'> AND price <= #{maxPrice}</if>" +
"<if test='roomType != null'> AND room_type = #{roomType}</if>" +
"<if test='keywords != null'> AND (title LIKE CONCAT('%',#{keywords},'%') OR address LIKE CONCAT('%',#{keywords},'%'))</if>" +
" ORDER BY create_time DESC" +
"</script>")
List<House> searchHouses(@Param("minPrice") BigDecimal minPrice,
@Param("maxPrice") BigDecimal maxPrice,
@Param("roomType") Integer roomType,
@Param("keywords") String keywords);
}
3.2 电子合约管理系统
3.2.1 合约模板设计
采用Freemarker动态生成PDF合约,核心流程:
- 制作合约模板文件(.ftl)
- 填充合约数据模型
- 使用Flying Saucer库渲染PDF
关键代码示例:
java复制public byte[] generateContract(ContractTemplate template, Map<String, Object> data) throws Exception {
Configuration cfg = new Configuration(Configuration.VERSION_2_3_30);
cfg.setClassForTemplateLoading(this.getClass(), "/templates");
Template temp = cfg.getTemplate(template.getPath());
StringWriter writer = new StringWriter();
temp.process(data, writer);
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(writer.toString());
renderer.layout();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
renderer.createPDF(baos);
return baos.toByteArray();
}
3.2.2 电子签名验证
采用国密SM2算法实现签名验证:
java复制public class SignVerifyUtil {
private static final SM2 sm2 = new SM2();
public static boolean verify(String content, String sign, String publicKey) {
byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
byte[] signBytes = Base64.getDecoder().decode(sign);
byte[] pubKeyBytes = Hex.decode(publicKey);
ECPublicKeyParameters pubKey = sm2.decodePublicKey(pubKeyBytes);
return sm2.verify(contentBytes, signBytes, pubKey);
}
}
4. 系统安全与性能优化
4.1 安全防护措施
4.1.1 防SQL注入
除使用MyBatis预编译语句外,额外添加过滤器进行参数校验:
java复制@WebFilter("/*")
public class SqlInjectionFilter implements Filter {
private static final Pattern SQL_PATTERN = Pattern.compile(
"('.+--)|(--)|(\\|)|(%7C)|(exec)|(execute)|(union)|(select)|(insert)|(delete)|(update)|(drop)|(alter)",
Pattern.CASE_INSENSITIVE);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
Enumeration<String> names = req.getParameterNames();
while (names.hasMoreElements()) {
String[] values = req.getParameterValues(names.nextElement());
for (String value : values) {
if (SQL_PATTERN.matcher(value).find()) {
throw new ServletException("检测到非法参数");
}
}
}
chain.doFilter(request, response);
}
}
4.1.2 敏感数据加密
采用AES加密存储租户身份证号等敏感信息:
java复制public class AesUtil {
private static final String KEY = "your-32-byte-key";
private static final String IV = "your-16-byte-iv";
public static String encrypt(String plainText) throws Exception {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,
new SecretKeySpec(KEY.getBytes(), "AES"),
new IvParameterSpec(IV.getBytes()));
byte[] encrypted = cipher.doFinal(plainText.getBytes());
return Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String encryptedText) throws Exception {
// 解密逻辑...
}
}
4.2 性能优化实践
4.2.1 缓存策略设计
采用多级缓存架构:
- 本地Caffeine缓存高频访问的房源详情
- Redis缓存热门房源列表
- 数据库查询结果缓存
配置示例:
java复制@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CaffeineCacheManager caffeineCacheManager() {
Caffeine<Object, Object> caffeine = Caffeine.newBuilder()
.initialCapacity(100)
.maximumSize(500)
.expireAfterWrite(10, TimeUnit.MINUTES);
return new CaffeineCacheManager("houseDetail", caffeine);
}
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
4.2.2 图片存储优化
采用阿里云OSS存储房源图片,前端通过签名URL访问:
java复制public class OssService {
private final OSS ossClient;
private final String bucketName;
public String generatePresignedUrl(String objectKey) {
Date expiration = new Date(System.currentTimeMillis() + 3600 * 1000);
GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, objectKey);
request.setExpiration(expiration);
return ossClient.generatePresignedUrl(request).toString();
}
public void uploadImage(String objectKey, InputStream stream) {
ossClient.putObject(bucketName, objectKey, stream);
}
}
5. 典型问题排查实录
5.1 Thymeleaf模板解析异常
现象:页面渲染时报模板解析错误,提示"Error resolving template"
排查过程:
- 检查模板文件是否放在resources/templates目录
- 确认Controller返回的视图名称与模板文件名一致(不含.html后缀)
- 检查是否有Thymeleaf的依赖冲突
解决方案:
properties复制# 确保配置正确
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
5.2 并发签约冲突
现象:多个用户同时签约同一房源时出现数据不一致
解决方案:
- 数据库层面添加乐观锁:
sql复制ALTER TABLE house ADD COLUMN version INT DEFAULT 0;
- 更新时检查版本号:
java复制@Update("UPDATE house SET status=1, version=version+1 WHERE id=#{id} AND version=#{version}")
int updateWithVersion(@Param("id") Long id, @Param("version") int version);
- 前端添加二次确认机制
5.3 PDF生成中文乱码
问题原因:Flying Saucer默认不支持中文字体
解决方案:
- 将字体文件放入resources/fonts目录
- 配置CSS指定中文字体:
css复制@font-face {
font-family: "SimSun";
src: url(/fonts/simsun.ttf);
-fs-pdf-font-embed: embed;
-fs-pdf-font-encoding: Identity-H;
}
body {
font-family: "SimSun";
}
6. 项目部署与运维
6.1 生产环境部署方案
推荐采用Docker Compose部署:
yaml复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
volumes:
- ./application-prod.yml:/config/application.yml
command: ["java", "-jar", "/app/rental-system.jar", "--spring.config.location=/config/application.yml"]
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: yourpassword
MYSQL_DATABASE: rental_db
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
6.2 监控配置
使用SpringBoot Actuator+Prometheus+Grafana搭建监控看板:
- 添加依赖:
xml复制<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
- 配置application.yml:
yaml复制management:
endpoints:
web:
exposure:
include: health,info,prometheus
metrics:
tags:
application: rental-system
- Grafana仪表盘关键指标:
- JVM内存使用率
- 数据库连接池活跃连接数
- 接口响应时间P99
- 房源搜索QPS
7. 扩展功能建议
7.1 智能推荐系统
基于用户行为数据实现个性化推荐:
- 收集用户浏览、收藏、签约等行为
- 使用协同过滤算法计算房源相似度
- 实现推荐API接口:
java复制@GetMapping("/recommend")
public List<House> recommendHouses(@RequestParam Long userId) {
// 1. 获取用户特征向量
double[] userVector = userService.getUserVector(userId);
// 2. 计算相似房源
return houseService.findSimilarHouses(userVector, 10);
}
7.2 微信小程序端
使用uni-app快速开发跨平台小程序:
- 封装统一API客户端:
javascript复制const api = {
searchHouses(params) {
return uni.request({
url: '/api/houses/search',
method: 'GET',
data: params
})
}
}
- 实现房源列表页:
vue复制<template>
<scroll-view @scrolltolower="loadMore">
<house-card v-for="item in list" :key="item.id" :data="item"/>
</scroll-view>
</template>
7.3 区块链存证
将电子合约哈希值上链存证:
- 使用Fabric SDK连接区块链网络
- 合约存证核心方法:
java复制public class BlockchainService {
public String storeHash(String contractHash) throws Exception {
Network network = connectNetwork();
Contract contract = network.getContract("rental-contract");
byte[] response = contract.submitTransaction("storeHash", contractHash);
return new String(response);
}
}
在开发这个系统的过程中,最大的体会是业务逻辑的严谨性比技术实现更重要。比如在合约管理模块,我们最初忽略了租期重叠校验,导致出现同一房源被重复出租的情况。后来通过添加租期冲突检测算法解决了这个问题:
java复制public boolean checkRentConflict(Long houseId, LocalDate startDate, LocalDate endDate) {
return contractMapper.existsOverlapContract(houseId, startDate, endDate) > 0;
}
另一个实用技巧是使用Hutool工具类简化开发,比如其中的ExcelUtil可以快速实现房源数据的导入导出功能:
java复制// 导出房源列表
List<House> houses = houseService.listAll();
ExcelWriter writer = ExcelUtil.getWriter();
writer.addHeaderAlias("title", "房源标题");
writer.write(houses, true);
writer.flush(outputStream);
writer.close();
