作为一名经历过多次毕业设计指导的开发者,我深知一个完整的毕业设计系统需要兼顾技术深度和实用价值。这个基于SSM+Vue的酒店式公寓服务系统,本质上是一个面向现代化公寓管理的全流程解决方案。随着城市化进程加快,酒店式公寓作为新型居住形态正在快速普及,但传统的人工管理方式已经难以满足高效运营的需求。
在实际调研中,我发现当前公寓管理普遍存在几个痛点:租客信息分散、房间状态更新滞后、费用结算效率低下、设备报修流程繁琐。这些问题直接影响了运营效率和住户体验。而市面上现有的管理系统要么功能单一,要么价格昂贵,对中小型公寓运营商极不友好。
这个系统采用前后端分离架构,前端使用Vue.js构建响应式界面,后端基于Spring+SpringMVC+MyBatis框架实现业务逻辑。特别值得一提的是,我们针对公寓管理的特殊需求,设计了动态定价、智能门锁对接、水电费自动计算等特色功能模块。在技术选型上,我们放弃了传统的JSP方案,转而采用更现代化的Vue+SSM组合,这使得系统在维护性和扩展性上都有显著优势。
选择SSM+Vue这套技术组合主要基于以下考量:
后端技术栈:
前端技术栈:
技术选型心得:在实际开发中,我们曾考虑过Spring Boot简化配置,但最终选择传统SSM是为了更清晰地展示各层配置过程。Vue则放弃了最新的3.x版本,因为学校实验室的Node.js环境普遍较旧,2.x的兼容性更好。
系统核心模块设计如下图所示(文字描述):
code复制1. 用户管理模块
- 租客注册/登录
- 权限分级(租客/管理员/保洁)
- 个人信息维护
2. 房间管理模块
- 房间状态监控(空闲/已租/维修)
- 房间信息维护
- 智能门锁对接
3. 订单管理模块
- 预订流程
- 合同生成
- 续租/退租处理
4. 费用管理模块
- 租金计算
- 水电费自动统计
- 在线支付对接
5. 服务管理模块
- 保洁服务预约
- 设备报修流程
- 投诉建议处理
这种模块化设计使得系统功能高度内聚,后期维护时能够快速定位问题。特别是在费用计算模块,我们采用了策略模式,可以灵活支持不同房间类型的计价规则。
数据库设计是系统稳定性的基石,我们主要设计了以下表结构:
用户表(users)
sql复制CREATE TABLE `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`real_name` varchar(50) DEFAULT NULL,
`phone` varchar(20) NOT NULL,
`id_card` varchar(18) DEFAULT NULL,
`role` enum('tenant','admin','cleaner') NOT NULL,
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`),
KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
房间表(rooms)
sql复制CREATE TABLE `rooms` (
`room_id` int(11) NOT NULL AUTO_INCREMENT,
`room_number` varchar(10) NOT NULL,
`room_type` enum('standard','deluxe','suite') NOT NULL,
`floor` int(11) NOT NULL,
`status` enum('vacant','occupied','maintenance') NOT NULL DEFAULT 'vacant',
`daily_price` decimal(10,2) NOT NULL,
`facilities` json DEFAULT NULL,
PRIMARY KEY (`room_id`),
UNIQUE KEY `idx_room_number` (`room_number`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
订单表(orders)
sql复制CREATE TABLE `orders` (
`order_id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`room_id` int(11) NOT NULL,
`check_in_date` date NOT NULL,
`check_out_date` date NOT NULL,
`total_amount` decimal(10,2) NOT NULL,
`status` enum('pending','confirmed','canceled','completed') NOT NULL DEFAULT 'pending',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`order_id`),
KEY `idx_user` (`user_id`),
KEY `idx_room` (`room_id`),
KEY `idx_dates` (`check_in_date`,`check_out_date`),
CONSTRAINT `fk_order_room` FOREIGN KEY (`room_id`) REFERENCES `rooms` (`room_id`),
CONSTRAINT `fk_order_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
在实际开发中,我们遇到了几个性能瓶颈问题及解决方案:
房间查询慢问题:初期没有为status字段建立索引,导致筛选可用房间时响应时间超过2秒。通过添加组合索引(status, room_type),查询速度提升到200ms内。
订单统计延迟:月报表生成时全表扫描导致超时。解决方案是创建预计算表,每天凌晨通过定时任务更新统计数据。
连接池配置:默认的DBCP配置在高并发下表现不佳,调整为HikariCP后,TPS从50提升到120。
数据库设计经验:对于状态字段一定要建立合适索引;金额类字段使用decimal而非float;时间范围查询需要特别注意索引设计。
SSM框架的整合是关键步骤,以下是核心配置要点:
applicationContext.xml关键配置
xml复制<!-- 数据源配置 -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/apartment_db?useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="maximumPoolSize" value="20"/>
</bean>
<!-- MyBatis配置 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
<property name="typeAliasesPackage" value="com.apartment.entity"/>
</bean>
<!-- 事务管理 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
以房间预订为例,展示典型业务逻辑的实现:
RoomServiceImpl.java
java复制@Service
@Transactional
public class RoomServiceImpl implements RoomService {
@Autowired
private RoomMapper roomMapper;
@Autowired
private OrderMapper orderMapper;
@Override
public BookingResult bookRoom(BookingRequest request) {
// 1. 检查房间可用性
Room room = roomMapper.selectById(request.getRoomId());
if (room == null || !"vacant".equals(room.getStatus())) {
throw new BusinessException("房间不可预订");
}
// 2. 检查日期冲突
int conflictCount = orderMapper.countDateConflict(
request.getRoomId(),
request.getCheckInDate(),
request.getCheckOutDate());
if (conflictCount > 0) {
throw new BusinessException("该时间段已被预订");
}
// 3. 计算费用
long days = ChronoUnit.DAYS.between(
request.getCheckInDate(),
request.getCheckOutDate());
BigDecimal total = room.getDailyPrice().multiply(new BigDecimal(days));
// 4. 创建订单
Order order = new Order();
order.setUserId(request.getUserId());
order.setRoomId(request.getRoomId());
order.setCheckInDate(request.getCheckInDate());
order.setCheckOutDate(request.getCheckOutDate());
order.setTotalAmount(total);
orderMapper.insert(order);
// 5. 更新房间状态
room.setStatus("occupied");
roomMapper.update(room);
return new BookingResult(order.getOrderId(), total);
}
}
这段代码展示了典型的事务处理场景,包含了参数校验、业务计算、数据持久化等完整流程。特别要注意的是:
前端项目采用标准的Vue-cli生成结构:
code复制src/
├── assets/ # 静态资源
├── components/ # 公共组件
│ ├── DatePicker.vue
│ ├── RoomCard.vue
│ └── PaymentDialog.vue
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── utils/ # 工具函数
├── views/ # 页面组件
│ ├── Home.vue # 首页
│ ├── Room/ # 房间管理
│ ├── Order/ # 订单管理
│ └── User/ # 用户中心
└── App.vue # 根组件
以房间预订表单组件为例:
BookingForm.vue
vue复制<template>
<div class="booking-form">
<el-form :model="form" :rules="rules" ref="formRef">
<el-form-item label="入住日期" prop="checkInDate">
<el-date-picker
v-model="form.checkInDate"
type="date"
:disabled-date="disabledCheckInDate"
placeholder="选择入住日期">
</el-date-picker>
</el-form-item>
<el-form-item label="离店日期" prop="checkOutDate">
<el-date-picker
v-model="form.checkOutDate"
type="date"
:disabled-date="disabledCheckOutDate"
placeholder="选择离店日期">
</el-date-picker>
</el-form-item>
<el-button type="primary" @click="submitForm">立即预订</el-button>
</el-form>
</div>
</template>
<script>
export default {
props: {
roomId: {
type: Number,
required: true
}
},
data() {
return {
form: {
checkInDate: '',
checkOutDate: ''
},
rules: {
checkInDate: [
{ required: true, message: '请选择入住日期', trigger: 'blur' }
],
checkOutDate: [
{ required: true, message: '请选择离店日期', trigger: 'blur' },
{ validator: this.validateDates, trigger: 'change' }
]
}
}
},
methods: {
disabledCheckInDate(time) {
return time.getTime() < Date.now() - 86400000;
},
disabledCheckOutDate(time) {
if (!this.form.checkInDate) return true;
const checkIn = new Date(this.form.checkInDate).getTime();
return time.getTime() <= checkIn;
},
validateDates(rule, value, callback) {
if (this.form.checkInDate && new Date(value) <= new Date(this.form.checkInDate)) {
callback(new Error('离店日期必须晚于入住日期'));
} else {
callback();
}
},
async submitForm() {
try {
await this.$refs.formRef.validate();
const res = await this.$http.post('/api/orders', {
roomId: this.roomId,
checkInDate: this.formatDate(this.form.checkInDate),
checkOutDate: this.formatDate(this.form.checkOutDate)
});
this.$message.success(`预订成功!订单号:${res.data.orderId}`);
this.$emit('booking-success');
} catch (error) {
this.$message.error(error.response?.data?.message || '预订失败');
}
},
formatDate(date) {
return date ? new Date(date).toISOString().split('T')[0] : '';
}
}
}
</script>
这个组件展示了几个关键实践:
bash复制# 下载JDK 1.8
wget https://download.oracle.com/otn/java/jdk/8u301-b09/d3c52aa6bfa54d3ca74e617f18309292/jdk-8u301-linux-x64.tar.gz
# 解压并配置环境变量
tar -zxvf jdk-8u301-linux-x64.tar.gz -C /usr/local/
echo 'export JAVA_HOME=/usr/local/jdk1.8.0_301' >> /etc/profile
echo 'export PATH=$JAVA_HOME/bin:$PATH' >> /etc/profile
source /etc/profile
bash复制# 下载Tomcat 7
wget https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.109/bin/apache-tomcat-7.0.109.tar.gz
# 解压并配置
tar -zxvf apache-tomcat-7.0.109.tar.gz -C /opt/
export CATALINA_HOME=/opt/apache-tomcat-7.0.109
bash复制# Ubuntu安装MySQL 5.7
sudo apt-get update
sudo apt-get install mysql-server-5.7
# 安全配置
sudo mysql_secure_installation
# 创建数据库
mysql -u root -p -e "CREATE DATABASE apartment_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
使用JMeter进行压力测试,主要指标如下:
| 测试场景 | 并发用户数 | 平均响应时间 | 错误率 | TPS |
|---|---|---|---|---|
| 房间查询 | 100 | 238ms | 0% | 320 |
| 预订操作 | 50 | 512ms | 1.2% | 85 |
| 支付流程 | 30 | 1.2s | 0.5% | 25 |
优化建议:
java复制@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.apartment.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(metaData());
}
private ApiInfo metaData() {
return new ApiInfoBuilder()
.title("公寓管理系统API文档")
.description("毕业设计项目接口说明")
.version("1.0.0")
.build();
}
}
javascript复制// 在vue.config.js中配置
module.exports = {
devServer: {
before(app) {
app.get('/api/rooms', (req, res) => {
const data = Mock.mock({
'list|10': [{
'id|+1': 1,
'number': '@natural(101, 520)',
'type|1': ['standard', 'deluxe', 'suite'],
'price|300-800': 500,
'status|1': ['vacant', 'occupied']
}]
});
res.json(data);
});
}
}
}
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
java复制@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
.dateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
.serializationInclusion(JsonInclude.Include.NON_NULL);
converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
}
}
javascript复制// nginx配置
location / {
try_files $uri $uri/ /index.html;
}
这个基础系统还可以进一步扩展以下功能:
智能硬件对接:
数据分析模块:
移动端适配:
微服务改造:
在实际开发过程中,我最大的体会是:文档和注释的重要性怎么强调都不为过。特别是在团队协作时,清晰的接口文档和代码注释能节省大量沟通成本。另外,测试驱动开发(TDD)的模式虽然初期进度看起来慢一些,但后期调试时间大幅减少,整体效率反而更高。