1. 项目概述:前后端分离房屋交易平台系统
这个基于SpringBoot+Vue+MyBatis+MySQL的房屋交易平台系统,是我去年为一个本地房产中介公司开发的核心业务系统。相比传统单体架构,前后端分离的设计让我们的开发效率提升了近40%,特别是在应对频繁的需求变更时表现尤为突出。
系统主要解决了房产交易中的三大痛点:一是信息不透明导致的交易周期长,二是线下流程繁琐带来的效率低下,三是数据分散难以形成有效分析。通过将房源管理、客户跟进、合同生成等核心业务线上化,客户平均成交周期从原来的45天缩短到了28天。
提示:选择前后端分离架构时,建议先评估团队的技术栈匹配度。我们团队有3名专职前端和5名后端开发,这种人员配比能充分发挥分离架构的优势。
2. 技术栈选型解析
2.1 后端技术组合
SpringBoot 2.7.18作为基础框架,这个版本在稳定性和新特性之间取得了很好的平衡。我们特别看重它的自动配置能力,比如集成MyBatis时只需要简单的starter依赖:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
MyBatis-Plus 3.5.3作为ORM层,它的Lambda查询构建器让我们的DAO层代码量减少了60%。对于复杂的多表关联查询,我们采用了XML映射文件+注解的混合模式,例如房源详情页的SQL:
xml复制<select id="selectHouseDetail" resultMap="houseDetailMap">
SELECT h.*, a.name as area_name, u.real_name as owner_name
FROM house h
LEFT JOIN area a ON h.area_id = a.id
LEFT JOIN user u ON h.owner_id = u.id
WHERE h.id = #{id}
</select>
2.2 前端技术方案
Vue 3.2+组合式API让我们的前端组件更易于维护。特别值得一提的是使用了Pinia替代Vuex进行状态管理,配合axios拦截器实现的API请求层:
javascript复制// api/request.js
const service = axios.create({
baseURL: import.meta.env.VITE_APP_BASE_API,
timeout: 15000
})
service.interceptors.request.use(config => {
if (store.user.token) {
config.headers['X-Token'] = store.user.token
}
return config
})
Element Plus作为UI框架,其表单验证功能极大简化了房源发布页面的开发。我们还自定义了几个业务组件,如图片上传组件支持拖拽排序和预览:
vue复制<template>
<el-upload
v-model:file-list="fileList"
action="/api/upload"
list-type="picture-card"
:on-preview="handlePreview"
:before-upload="beforeUpload">
<el-icon><Plus /></el-icon>
</el-upload>
</template>
3. 核心模块设计与实现
3.1 房源管理模块
采用CQRS模式将读写操作分离。写操作使用严格的DTO验证:
java复制@PostMapping("/houses")
public Result addHouse(@Valid @RequestBody HouseCreateDTO dto) {
return Result.success(houseService.createHouse(dto));
}
读操作则通过Elasticsearch实现全文检索,我们为房源建立了如下索引:
json复制{
"mappings": {
"properties": {
"title": {"type": "text", "analyzer": "ik_max_word"},
"price": {"type": "double"},
"area": {"type": "integer"},
"location": {"type": "geo_point"}
}
}
}
3.2 交易流程引擎
基于状态模式实现的交易状态机是核心业务逻辑所在:
java复制public class TransactionStateMachine {
private TransactionState currentState;
public void nextStep(TransactionContext context) {
currentState.handle(context);
currentState = currentState.nextState();
}
}
配合工作流引擎,我们定义了如下状态流转规则:
| 当前状态 | 允许操作 | 下一状态 |
|---|---|---|
| 待签约 | 上传定金凭证 | 已付定金 |
| 已付定金 | 签署正式合同 | 等待过户 |
| 等待过户 | 上传过户材料 | 已完成 |
3.3 即时通讯系统
采用WebSocket实现的在线咨询功能,后端使用Spring的WebSocket支持:
java复制@Controller
public class ChatEndpoint {
@OnMessage
public void handleMessage(Session session, String msg) {
ChatMessage message = JSON.parseObject(msg, ChatMessage.class);
messageService.save(message);
brokerTemplate.convertAndSendToUser(
message.getToUserId(),
"/queue/chat",
message
);
}
}
前端使用SockJS作为降级方案,确保在HTTP环境下也能工作:
javascript复制const socket = new SockJS('/ws-chat')
const stompClient = Stomp.over(socket)
stompClient.connect({}, frame => {
stompClient.subscribe('/user/queue/chat', message => {
store.commit('addMessage', JSON.parse(message.body))
})
})
4. 数据库设计与优化
4.1 主要表结构
房源表采用了垂直分表设计,将基本信息和扩展信息分离:
sql复制CREATE TABLE `house` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`price` decimal(12,2) NOT NULL,
`area_id` int NOT NULL,
`owner_id` bigint NOT NULL,
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_area` (`area_id`),
KEY `idx_owner` (`owner_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `house_detail` (
`house_id` bigint NOT NULL,
`description` text,
`facilities` json DEFAULT NULL,
`photos` json DEFAULT NULL,
PRIMARY KEY (`house_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 查询优化实践
对于复杂的聚合查询,我们采用了物化视图技术。例如区域房价统计:
sql复制CREATE MATERIALIZED VIEW area_price_stats
REFRESH COMPLETE ON DEMAND
AS
SELECT
a.id as area_id,
a.name as area_name,
COUNT(h.id) as house_count,
AVG(h.price) as avg_price,
MAX(h.price) as max_price,
MIN(h.price) as min_price
FROM area a
LEFT JOIN house h ON a.id = h.area_id
GROUP BY a.id, a.name;
5. 部署架构与性能调优
5.1 容器化部署方案
使用Docker Compose编排服务,关键配置如下:
yaml复制version: '3'
services:
app:
image: openjdk:11-jre
ports:
- "8080:8080"
volumes:
- ./app.jar:/app.jar
command: java -jar /app.jar
depends_on:
- redis
- mysql
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: house_db
volumes:
- mysql_data:/var/lib/mysql
volumes:
mysql_data:
5.2 Nginx配置要点
前后端分离部署时,Nginx的配置尤为关键:
nginx复制server {
listen 80;
server_name house.example.com;
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
}
6. 典型问题排查实录
6.1 MyBatis缓存问题
我们曾遇到更新操作后查询结果不一致的问题,最终发现是MyBatis二级缓存导致。解决方案:
- 在配置文件中显式关闭二级缓存:
xml复制<settings>
<setting name="cacheEnabled" value="false"/>
</settings>
- 对于需要缓存的查询,手动控制缓存范围:
java复制@CacheNamespace(flushInterval = 3600000)
public interface HouseMapper {
@Options(useCache = true)
@Select("SELECT * FROM house WHERE id = #{id}")
House selectById(Long id);
}
6.2 Vue响应式丢失
在编辑房源信息时,表单数据突然失去响应性。原因是直接修改了数组元素:
javascript复制// 错误做法
this.house.photos[0] = newPhoto
// 正确做法
this.house.photos = [
newPhoto,
...this.house.photos.slice(1)
]
7. 安全防护措施
7.1 接口安全设计
采用JWT+RBAC的鉴权方案,Spring Security配置核心:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/admin/**").hasRole("ADMIN")
.antMatchers("/api/broker/**").hasRole("BROKER")
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()));
return http.build();
}
}
7.2 数据脱敏处理
在返回用户敏感信息时,使用Jackson的JsonSerializer实现自动脱敏:
java复制public class MobileSerializer extends JsonSerializer<String> {
@Override
public void serialize(String value, JsonGenerator gen, SerializerProvider provider) {
try {
gen.writeString(value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2"));
} catch (Exception e) {
gen.writeString("");
}
}
}
8. 监控与日志方案
8.1 Prometheus监控
通过Micrometer暴露SpringBoot指标:
java复制@Bean
MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
return registry -> registry.config().commonTags(
"application", "house-trade"
);
}
对应的Grafana监控面板包含以下关键指标:
- 接口响应时间P99
- JVM内存使用率
- MySQL连接池活跃数
- Redis缓存命中率
8.2 日志收集架构
采用ELK方案收集日志,Logstash配置示例:
ruby复制input {
file {
path => "/var/log/spring/*.log"
codec => json
}
}
filter {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{NUMBER:pid} --- \[%{DATA:thread}\] %{DATA:class} : %{GREEDYDATA:msg}" }
}
}
9. 持续集成实践
GitLab CI/CD流水线包含三个阶段:
yaml复制stages:
- build
- test
- deploy
build-backend:
stage: build
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
test-e2e:
stage: test
image: cypress/included:12.0.0
script:
- npm install
- npx cypress run
10. 移动端适配方案
虽然主要是PC端系统,但我们通过响应式布局支持移动访问。关键CSS方案:
css复制@media (max-width: 768px) {
.house-card {
width: 100%;
margin-bottom: 1rem;
}
.search-form {
flex-direction: column;
}
}
对于照片展示,使用了自适应图片组件:
vue复制<template>
<img
:srcset="`${small} 480w, ${medium} 1024w`"
sizes="(max-width: 600px) 480px, 800px"
:src="defaultImg"
alt="房源图片"
>
</template>
在项目上线后,我们持续收集用户反馈进行迭代。最近新增的VR看房功能使用了Three.js实现,将用户停留时长提升了25%。这个项目的成功让我深刻体会到,合适的技术选型加上持续的优化迭代,才能真正打造出有价值的商业系统。
