作为一名长期从事企业级应用开发的工程师,我最近完成了一个基于SpringBoot+Vue+MyBatis的房屋租赁管理系统。这个系统从设计到实现历时三个月,期间踩过不少坑,也积累了一些值得分享的经验。不同于简单的CRUD系统,这个项目需要处理复杂的业务流程、多角色权限控制和电子合同签署等企业级需求。
系统采用经典的前后端分离架构,后端基于SpringBoot 2.7.3构建,前端使用Vue 3的组合式API,ORM层采用MyBatis-Plus 3.5.1增强数据访问能力。数据库选用MySQL 8.0,主要考虑到其在事务处理和复杂查询方面的稳定性。整个系统部署在阿里云ECS上,通过Nginx实现反向代理和负载均衡。
房源管理是整个系统的基础模块,我们设计了包含7个核心字段的数据表(如正文所示)。在实际开发中,有几个关键点值得注意:
java复制public enum PropertyStatus {
AVAILABLE("待租"),
RENTED("已租"),
MAINTENANCE("维修中");
private final String desc;
// 构造方法、getter省略
}
租户表设计有几个安全相关的重点:
java复制public class EncryptTypeHandler extends BaseTypeHandler<String> {
private final KmsService kmsService;
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
String parameter, JdbcType jdbcType) {
ps.setString(i, kmsService.encrypt(parameter));
}
// 其他方法省略
}
java复制public TenantDTO desensitize() {
this.idCard = "****" + this.idCard.substring(14);
this.phone = this.phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
return this;
}
合同管理是最复杂的模块,主要难点在于:
sql复制CREATE TABLE contract_template (
id BIGINT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
content TEXT NOT NULL,
version VARCHAR(20) NOT NULL,
is_active BOOLEAN DEFAULT FALSE
);
java复制public interface RentCalculationStrategy {
BigDecimal calculate(RentCalculateContext context);
}
@Component
@StrategyType("FIXED_RATE")
public class FixedRateStrategy implements RentCalculationStrategy {
// 实现略
}
java复制@DS("master") // 数据源注解
@Transactional(rollbackFor = Exception.class)
public void signContract(Long contractId) {
// 1. 更新合同状态
// 2. 生成账单
// 3. 修改房源状态
}
javascript复制// stores/property.js
export const usePropertyStore = defineStore('property', {
state: () => ({
filters: {
minPrice: null,
maxPrice: null,
status: 'AVAILABLE'
}
}),
actions: {
async fetchProperties() {
// API调用
}
}
})
javascript复制const { defineInputBinds, handleSubmit } = useForm({
validationSchema: toTypedSchema(yup.object({
phone: yup.string().required().matches(/^1[3-9]\d{9}$/),
email: yup.string().email()
}))
})
vue复制<template>
<RecycleScroller
:items="properties"
:item-size="72"
key-field="id"
v-slot="{ item }"
>
<PropertyCard :property="item" />
</RecycleScroller>
</template>
sql复制-- 房源查询高频条件
ALTER TABLE property ADD INDEX idx_search (status, location, rent_price);
-- 合同查询索引
ALTER TABLE lease_contract ADD INDEX idx_tenant (tenant_id, start_date);
sql复制CREATE TABLE payment_record_202301 (
LIKE payment_record INCLUDING INDEXES
) PARTITION BY RANGE (YEAR(pay_date)*100 + MONTH(pay_date));
xml复制<resultMap id="ContractDetailMap" type="ContractDTO">
<association property="property" select="getPropertyById" column="property_id"/>
<association property="tenant" select="getTenantById" column="tenant_id"/>
</resultMap>
java复制@Select("SELECT * FROM property")
@Options(resultSetType = ResultSetType.FORWARD_ONLY, fetchSize = 1000)
@ResultType(Property.class)
void exportProperties(ResultHandler<Property> handler);
sql复制CREATE MATERIALIZED VIEW property_stats AS
SELECT
location,
AVG(rent_price) AS avg_price,
COUNT(*) AS total
FROM property
WHERE status = 'AVAILABLE'
GROUP BY location;
yaml复制version: '3'
services:
app:
image: registry.cn-hangzhou.aliyuncs.com/your-repo/rental:${TAG}
ports:
- "8080:8080"
depends_on:
- redis
- mysql
environment:
- SPRING_PROFILES_ACTIVE=prod
mysql:
image: mysql:8.0
volumes:
- mysql_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
yaml复制stages:
- build
- test
- deploy
backend-test:
stage: test
script:
- mvn test -B
- sonar-scanner -Dsonar.projectKey=rental-backend
frontend-build:
stage: build
script:
- cd frontend
- npm install
- npm run build
properties复制management.endpoint.health.probes.enabled=true
management.health.db.enabled=true
management.health.redis.enabled=true
yaml复制- alert: HighErrorRate
expr: rate(http_server_requests_errors_total[1m]) > 0.1
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate on {{ $labels.instance }}"
问题现象:合同签署流程中,约5%的请求出现签名超时。
排查过程:
解决方案:
properties复制# 调整HikariCP配置
spring.datasource.hikari.maximum-pool-size=50
spring.datasource.hikari.connection-timeout=30000
问题现象:当房源数据超过10万条时,搜索接口响应时间超过3秒。
优化步骤:
优化后效果:P99响应时间从3200ms降至180ms。
问题现象:服务运行3天后,内存占用达到90%以上。
排查工具:
定位原因:未关闭的PDFBox文档对象持续累积。
修复方案:
java复制try (PDDocument doc = PDDocument.load(file)) {
// 处理文档
} // 自动关闭资源
java复制@Select("SELECT * FROM property WHERE status = #{status}")
List<Property> findByStatus(@Param("status") String status);
javascript复制import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(userInput);
java复制public String getMaskedPhone() {
return phone.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
}
sql复制CREATE TABLE audit_log (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
action VARCHAR(50) NOT NULL,
ip_address VARCHAR(45) NOT NULL,
create_time DATETIME NOT NULL,
params JSON
);
当前单体架构的痛点:
改造方案:
按业务边界拆分服务:
服务通信方式:
python复制# 使用sklearn实现简单预测模型
from sklearn.ensemble import RandomForestRegressor
model = RandomForestRegressor()
model.fit(X_train, y_train)
在项目开发过程中,最大的体会是:企业级系统开发不能只关注功能实现,必须从一开始就考虑性能、安全、可维护性等非功能性需求。比如我们在第一期迭代就建立了完整的监控体系,这在后期排查问题时发挥了巨大作用。另一个重要经验是:数据库设计要预留20%的扩展空间,我们的合同表就经历了三次结构调整,因为初期没有预见到电子签名带来的复杂需求变更。