1. 项目概述
这个智能租房网站项目采用SSM(Spring+SpringMVC+MyBatis)后端框架和VUE前端框架的组合架构,是一个典型的计算机专业毕业设计选题。作为全栈项目,它涵盖了从数据库设计到前后端交互的完整开发流程,特别适合想要展示完整开发能力的学生。
我在实际开发中发现,这类项目最考验的不是单一技术的深度,而是如何将不同技术栈有机整合。SSM框架提供了稳定的后端服务,而VUE则让前端交互更加流畅。两者的结合既符合企业主流技术选型,又能体现学生的全栈思维。
2. 技术架构解析
2.1 后端SSM框架选型
Spring框架作为核心容器,负责管理所有Bean的生命周期。我特别推荐使用Spring 5.x版本,因为它对注解支持更完善。在实际配置时,记得在applicationContext.xml中明确定义组件扫描路径:
xml复制<context:component-scan base-package="com.rental"/>
SpringMVC处理Web请求层,这里有个实用技巧:在springmvc.xml中配置静态资源过滤,避免VUE打包后的资源被拦截:
xml复制<mvc:resources mapping="/static/**" location="/static/"/>
MyBatis作为ORM框架,建议配合PageHelper分页插件使用。在mybatis-config.xml中添加如下配置:
xml复制<plugins>
<plugin interceptor="com.github.pagehelper.PageInterceptor">
<property name="reasonable" value="true"/>
</plugin>
</plugins>
2.2 前端VUE框架实现
VUE 2.x版本足够满足毕业设计需求。项目结构建议采用:
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
└── views/ # 页面组件
关键点在于axios的全局配置。在main.js中添加:
javascript复制axios.defaults.baseURL = 'http://localhost:8080'
axios.interceptors.request.use(config => {
config.headers['Authorization'] = localStorage.getItem('token')
return config
})
3. 核心功能实现
3.1 智能推荐模块
基于用户浏览历史的协同过滤算法实现:
java复制public List<House> recommendHouses(Long userId) {
// 1. 获取用户历史浏览记录
List<Long> viewedIds = historyMapper.selectViewedIds(userId);
// 2. 查找相似用户
List<Long> similarUserIds = historyMapper.findSimilarUsers(viewedIds);
// 3. 获取推荐房源
return houseMapper.selectRecommendedHouses(similarUserIds);
}
3.2 地图找房功能
集成高德地图API的关键步骤:
- 在index.html引入SDK
html复制<script src="https://webapi.amap.com/maps?v=2.0&key=您申请的key"></script>
- VUE组件中初始化地图
javascript复制mounted() {
this.map = new AMap.Map('map-container', {
zoom: 12,
center: [116.397428, 39.90923]
});
// 添加房源标记点
this.houses.forEach(house => {
new AMap.Marker({
position: [house.longitude, house.latitude],
map: this.map
});
});
}
3.3 在线签约系统
使用PDF.js实现电子合同签署:
javascript复制// 生成合同PDF
generateContract() {
const doc = new jsPDF();
doc.text(20, 20, '租房合同');
doc.save('contract.pdf');
}
// 签名处理
handleSign(canvas) {
const signature = canvas.toDataURL();
this.contract.signature = signature;
}
4. 数据库设计要点
4.1 主要表结构
用户表设计示例:
sql复制CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`phone` varchar(20) NOT NULL,
`real_name` varchar(50) DEFAULT NULL,
`id_card` varchar(18) DEFAULT NULL,
`create_time` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
4.2 优化建议
- 为常用查询字段添加索引:
sql复制ALTER TABLE `house` ADD INDEX `idx_location` (`city`, `district`);
- 使用冗余字段减少联表查询:
sql复制ALTER TABLE `order` ADD `landlord_name` VARCHAR(50) COMMENT '冗余房东姓名';
5. 开发注意事项
5.1 跨域问题解决方案
在后端配置CORS过滤器:
java复制@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.addAllowedOrigin("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
5.2 文件上传处理
SpringMVC文件上传配置:
java复制@PostMapping("/upload")
public Result upload(@RequestParam("file") MultipartFile file) {
String filename = UUID.randomUUID() + file.getOriginalFilename();
File dest = new File("/upload/" + filename);
file.transferTo(dest);
return Result.success("/upload/" + filename);
}
前端VUE组件:
vue复制<template>
<input type="file" @change="handleUpload">
</template>
<script>
methods: {
handleUpload(e) {
let formData = new FormData();
formData.append('file', e.target.files[0]);
axios.post('/upload', formData);
}
}
</script>
6. 毕业设计特别建议
6.1 文档编写要点
- 需求分析部分建议使用用例图
- 数据库设计展示ER图
- 系统架构图要体现前后端分离特点
- 核心算法需要伪代码说明
6.2 答辩准备技巧
- 准备两套演示数据:正常流程和异常流程
- 对关键SQL语句进行性能分析
- 记录开发过程中的难点及解决方案
- 对比同类产品的优缺点
实际开发中我发现,很多同学在分页查询时会出现N+1问题。解决方案是在MyBatis中使用嵌套查询:
xml复制<resultMap id="houseMap" type="House">
<collection property="images" column="id"
select="com.rental.mapper.ImageMapper.selectByHouseId"/>
</resultMap>
<select id="selectPage" resultMap="houseMap">
SELECT * FROM house LIMIT #{page},#{size}
</select>
对于VUE组件通信,推荐使用Vuex管理全局状态。在store.js中定义:
javascript复制const store = new Vuex.Store({
state: {
user: null
},
mutations: {
setUser(state, user) {
state.user = user
}
}
})
项目部署时,建议将前端打包文件放到Spring Boot的static目录下。在vue.config.js中配置:
javascript复制module.exports = {
outputDir: '../backend/src/main/resources/static',
devServer: {
proxy: 'http://localhost:8080'
}
}
数据库连接池配置是另一个需要注意的点。在application.properties中:
properties复制spring.datasource.druid.initial-size=5
spring.datasource.druid.max-active=20
spring.datasource.druid.max-wait=60000
spring.datasource.druid.time-between-eviction-runs-millis=60000
在开发支付功能时,建议使用沙箱环境测试。支付宝接口调用示例:
java复制public String createPay(Order order) {
AlipayClient alipayClient = new DefaultAlipayClient(
"https://openapi.alipaydev.com/gateway.do",
APP_ID,
APP_PRIVATE_KEY,
"json",
"UTF-8",
ALIPAY_PUBLIC_KEY,
"RSA2");
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
request.setReturnUrl(returnUrl);
request.setNotifyUrl(notifyUrl);
JSONObject bizContent = new JSONObject();
bizContent.put("out_trade_no", order.getNo());
bizContent.put("total_amount", order.getAmount());
bizContent.put("subject", "房租支付");
bizContent.put("product_code", "FAST_INSTANT_TRADE_PAY");
request.setBizContent(bizContent.toString());
return alipayClient.pageExecute(request).getBody();
}
短信验证码功能可以使用阿里云短信服务。关键实现:
java复制public void sendSms(String phone, String code) {
CommonRequest request = new CommonRequest();
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysAction("SendSms");
request.putQueryParameter("PhoneNumbers", phone);
request.putQueryParameter("SignName", "租房网");
request.putQueryParameter("TemplateCode", "SMS_123456");
request.putQueryParameter("TemplateParam", "{\"code\":\""+code+"\"}");
CommonResponse response = client.getCommonResponse(request);
if (!"OK".equals(response.getData())) {
throw new RuntimeException("短信发送失败");
}
}
对于房源搜索功能,Elasticsearch能显著提升性能。创建索引的DSL:
json复制PUT /house
{
"mappings": {
"properties": {
"title": {"type": "text", "analyzer": "ik_max_word"},
"address": {"type": "keyword"},
"price": {"type": "double"},
"location": {"type": "geo_point"}
}
}
}
Spring Data Elasticsearch集成配置:
java复制@Configuration
@EnableElasticsearchRepositories
public class EsConfig extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return new RestHighLevelClient(
RestClient.builder(new HttpHost("localhost", 9200))
);
}
}
定时任务可以使用Spring Scheduler实现。示例:
java复制@Scheduled(cron = "0 0 1 * * ?")
public void autoConfirmOrder() {
orderMapper.updateStatusByDate(
OrderStatus.WAITING_CONFIRM,
OrderStatus.COMPLETED,
LocalDate.now().minusDays(7)
);
}
对于权限控制,推荐使用Shiro框架。配置类示例:
java复制@Bean
public ShiroFilterFactoryBean shiroFilter() {
ShiroFilterFactoryBean factory = new ShiroFilterFactoryBean();
factory.setSecurityManager(securityManager());
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/api/**", "authc");
filterMap.put("/admin/**", "roles[admin]");
factory.setFilterChainDefinitionMap(filterMap);
return factory;
}
前端路由守卫配置:
javascript复制router.beforeEach((to, from, next) => {
if (to.meta.requiresAuth && !store.state.user) {
next('/login')
} else {
next()
}
})
日志记录建议使用AOP实现:
java复制@Aspect
@Component
public class LogAspect {
@AfterReturning(pointcut = "@annotation(com.rental.annotation.OperateLog)",
returning = "result")
public void afterReturning(JoinPoint jp, Object result) {
Log log = new Log();
log.setOperation(getMethodDescription(jp));
log.setParams(getParams(jp));
logMapper.insert(log);
}
}
性能监控可以使用Spring Boot Actuator:
properties复制management.endpoints.web.exposure.include=health,info,metrics
management.metrics.tags.application=${spring.application.name}
测试用例编写示例:
java复制@SpringBootTest
class HouseServiceTest {
@Autowired
private HouseService houseService;
@Test
void testSearch() {
PageInfo<House> page = houseService.search("朝阳", 1, 10);
assertThat(page.getList()).isNotEmpty();
}
}
前端组件测试:
javascript复制describe('HouseList.vue', () => {
it('renders houses when passed', () => {
const wrapper = shallowMount(HouseList, {
propsData: {
houses: mockHouses
}
})
expect(wrapper.findAll('.house-item').length).toBe(3)
})
})
项目打包部署时,建议使用Docker容器化:
dockerfile复制FROM openjdk:8-jdk-alpine
COPY target/rental-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
前端Dockerfile:
dockerfile复制FROM nginx:alpine
COPY dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
Nginx配置示例:
nginx复制server {
listen 80;
server_name rental.com;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
}
}