1. 项目概述
这个基于Java SpringBoot+Vue3+MyBatis的房产销售系统是一个典型的企业级前后端分离应用。我在实际开发中发现,这类系统在房地产中介、开发商销售部门等场景中需求非常普遍。系统采用MySQL作为数据库,完美契合了房产销售业务对数据一致性和事务处理的需求。
2. 技术架构解析
2.1 后端技术栈选型
SpringBoot作为后端框架的选择非常明智。我在多个项目中验证过,它的自动配置特性可以快速搭建起稳定的RESTful API服务。特别值得一提的是,Spring Security的集成让权限控制变得简单可靠。
MyBatis作为ORM框架,在处理复杂房产数据关联查询时表现出色。通过动态SQL,我们可以灵活应对各种条件组合查询需求,比如区域筛选、价格区间、户型组合等。
2.2 前端技术方案
Vue3的组合式API让前端开发效率大幅提升。在房产系统中,我特别推荐使用Pinia进行状态管理,它能很好地处理跨组件共享的筛选条件、用户偏好等数据。
Element Plus作为UI组件库,提供了丰富的表单组件和表格功能,非常适合房产信息的展示和编辑。我通常会自定义一些业务组件,比如户型图展示组件、楼盘地图组件等。
3. 核心功能实现
3.1 房源管理模块
房源信息的数据结构设计是关键。我通常会这样设计主要表结构:
sql复制CREATE TABLE `property` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`property_type` varchar(20) NOT NULL COMMENT '住宅/商铺/写字楼',
`price` decimal(12,2) NOT NULL,
`area` decimal(10,2) NOT NULL COMMENT '面积',
`room_config` varchar(20) COMMENT '户型',
`address` varchar(200) NOT NULL,
`longitude` decimal(10,6),
`latitude` decimal(10,6),
`status` tinyint NOT NULL DEFAULT 0 COMMENT '0-待售 1-已售',
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_area` (`area`),
KEY `idx_price` (`price`),
KEY `idx_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3.2 客户跟进功能
客户跟进记录需要特别注意数据安全和权限控制。我通常会在Service层实现这样的逻辑:
java复制@Transactional
public FollowRecord addFollowRecord(FollowRecordDTO dto, Long operatorId) {
// 验证操作人是否有权限跟进该客户
Customer customer = customerMapper.selectById(dto.getCustomerId());
if(customer == null) {
throw new BusinessException("客户不存在");
}
if(!customer.getSalesId().equals(operatorId)) {
throw new BusinessException("无权操作该客户");
}
FollowRecord record = new FollowRecord();
BeanUtils.copyProperties(dto, record);
record.setOperatorId(operatorId);
record.setFollowTime(new Date());
followRecordMapper.insert(record);
// 更新客户最后跟进时间
customer.setLastFollowTime(record.getFollowTime());
customerMapper.updateById(customer);
return record;
}
4. 前后端交互设计
4.1 API接口规范
我习惯采用RESTful风格设计API,以下是一个典型的房产列表接口设计:
java复制@GetMapping("/properties")
public PageResult<PropertyVO> listProperties(
@RequestParam(required = false) String keyword,
@RequestParam(required = false) String region,
@RequestParam(required = false) BigDecimal minPrice,
@RequestParam(required = false) BigDecimal maxPrice,
@RequestParam(required = false) String propertyType,
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<Property> properties = propertyService.listByConditions(
keyword, region, minPrice, maxPrice, propertyType);
List<PropertyVO> voList = properties.stream()
.map(this::convertToVO)
.collect(Collectors.toList());
PageInfo<Property> pageInfo = new PageInfo<>(properties);
return new PageResult<>(
pageInfo.getTotal(),
voList,
pageNum,
pageSize);
}
4.2 前端请求封装
在Vue3中,我会这样封装API请求:
javascript复制// src/api/property.js
import request from '@/utils/request'
export function fetchProperties(params) {
return request({
url: '/api/properties',
method: 'get',
params
})
}
// 在组件中使用
import { ref } from 'vue'
import { fetchProperties } from '@/api/property'
const loading = ref(false)
const tableData = ref([])
const total = ref(0)
const getList = async (queryParams) => {
try {
loading.value = true
const res = await fetchProperties(queryParams)
tableData.value = res.data.list
total.value = res.data.total
} catch (error) {
console.error('获取房源列表失败:', error)
} finally {
loading.value = false
}
}
5. 性能优化实践
5.1 数据库优化
对于房产系统这类读多写少的应用,我通常会采取以下优化措施:
- 合理设计索引:除了主键索引外,高频查询条件如区域、价格、面积等都需要建立索引
- 使用查询缓存:对变化不频繁的基础数据如区域字典、楼盘字典等启用MyBatis二级缓存
- 分库分表策略:当数据量超过500万时,考虑按城市分库,按区域分表
5.2 前端性能优化
- 图片懒加载:房产图片使用IntersectionObserver实现懒加载
- 虚拟列表:长列表使用vue-virtual-scroller优化渲染性能
- 路由懒加载:Vue路由配置使用动态import实现按需加载
javascript复制// 路由配置示例
const routes = [
{
path: '/properties',
component: () => import('@/views/property/List.vue')
},
{
path: '/property/:id',
component: () => import('@/views/property/Detail.vue')
}
]
6. 安全防护措施
6.1 防止SQL注入
MyBatis中使用#{}而不是${}来避免SQL注入:
xml复制<select id="selectByConditions" resultMap="BaseResultMap">
SELECT * FROM property
WHERE 1=1
<if test="region != null">
AND region = #{region}
</if>
<if test="minPrice != null">
AND price >= #{minPrice}
</if>
<!-- 其他条件 -->
</select>
6.2 XSS防护
在Vue3中,默认已经对绑定值进行了HTML转义。对于需要显示富文本的场景,可以使用v-html配合DOMPurify:
javascript复制import DOMPurify from 'dompurify'
const cleanHtml = DOMPurify.sanitize(dirtyHtml)
7. 部署方案
7.1 后端部署
我推荐使用Docker部署SpringBoot应用:
dockerfile复制FROM openjdk:11-jre
WORKDIR /app
COPY target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
7.2 前端部署
Vue3项目构建后可以直接用Nginx部署:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
8. 常见问题解决
8.1 MyBatis关联查询N+1问题
使用<collection>和<association>标签配合@ResultMap解决:
xml复制<resultMap id="PropertyDetailMap" type="com.example.PropertyDTO">
<id column="id" property="id"/>
<!-- 其他字段映射 -->
<collection property="images" ofType="com.example.PropertyImage"
select="selectImagesByPropertyId" column="id"/>
</resultMap>
<select id="selectDetailById" resultMap="PropertyDetailMap">
SELECT * FROM property WHERE id = #{id}
</select>
<select id="selectImagesByPropertyId" resultType="com.example.PropertyImage">
SELECT * FROM property_image WHERE property_id = #{id}
</select>
8.2 Vue3组件通信
对于跨多级组件通信,我推荐使用provide/inject:
javascript复制// 父组件
import { provide } from 'vue'
export default {
setup() {
const filters = ref({})
provide('propertyFilters', filters)
}
}
// 子组件
import { inject } from 'vue'
export default {
setup() {
const filters = inject('propertyFilters')
return { filters }
}
}
9. 扩展功能建议
在实际项目中,我通常会根据客户需求添加以下扩展功能:
- 微信小程序端:使用uni-app开发配套小程序,实现移动端看房
- VR看房:集成第三方VR服务,提供沉浸式看房体验
- 数据分析:使用ECharts实现销售数据可视化分析
- 电子签约:集成CA认证实现线上合同签署
10. 开发心得
在开发这类系统时,我有几点深刻体会:
- 数据库设计阶段就要充分考虑扩展性,比如预留足够多的扩展字段
- 前后端定好接口规范后,最好使用Swagger或YApi进行文档化管理
- 对于复杂的业务逻辑,一定要写单元测试,SpringBoot的@Test和JUnit5配合使用效果很好
- 前端组件要尽可能复用,比如筛选条件组件可以在列表页和地图找房页共用
一个实用的技巧是:在开发初期就建立完整的数据字典,包括房源状态、客户来源等枚举值,这样可以避免后期频繁修改代码。
