最近在做一个民宿租赁系统的项目,采用Spring Boot+Vue.js+MySQL的技术栈,前后端分离架构。这个系统主要解决传统民宿管理中的几个痛点:信息更新不及时、订单处理效率低、用户体验差等问题。系统上线后,房东可以方便地管理房源,租客能快速找到心仪的民宿,管理员也能高效处理各类业务。
我在开发过程中踩了不少坑,也积累了一些经验。下面就从技术选型、核心功能实现、关键代码解析等方面,详细分享这个项目的开发过程。如果你也在做类似的项目,这些经验应该能帮你少走弯路。
选择Spring Boot作为后端框架主要考虑以下几点:
Vue.js作为前端框架的优势:
数据库采用MySQL,主要考虑其成熟稳定、社区支持好。在设计表结构时特别注意了以下几点:
提示:在设计JSON字段时,MySQL 5.7+版本才原生支持,低版本需要考虑其他方案。
系统采用JWT(JSON Web Token)实现认证,关键实现如下:
java复制// JWT工具类示例
public class JwtUtil {
private static final String SECRET_KEY = "your-secret-key";
private static final long EXPIRATION_TIME = 864_000_000; // 10天
public static String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>();
return Jwts.builder()
.setClaims(claims)
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static Boolean validateToken(String token, UserDetails userDetails) {
final String username = extractUsername(token);
return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
}
}
权限控制使用Spring Security的注解方式:
java复制@PreAuthorize("hasRole('LANDLORD')")
@PostMapping("/properties")
public ResponseEntity<?> addProperty(@RequestBody PropertyDTO propertyDTO) {
// 房东添加房源逻辑
}
房源管理是系统的核心功能,主要接口包括:
java复制@GetMapping("/properties")
public Page<PropertyVO> getProperties(
@RequestParam(required = false) String location,
@RequestParam(required = false) BigDecimal minPrice,
@RequestParam(required = false) BigDecimal maxPrice,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size) {
// 构建查询条件
Specification<Property> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (location != null) {
predicates.add(cb.like(root.get("location"), "%" + location + "%"));
}
// 其他条件...
return cb.and(predicates.toArray(new Predicate[0]));
};
return propertyService.findAll(spec, PageRequest.of(page, size));
}
java复制@GetMapping("/properties/{id}")
public PropertyDetailVO getPropertyDetail(@PathVariable Long id) {
Property property = propertyService.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("Property not found"));
return convertToDetailVO(property);
}
java复制@PostMapping("/properties")
@Transactional
public ResponseEntity<?> createProperty(@Valid @RequestBody PropertyDTO dto) {
Property property = convertToEntity(dto);
property.setOwner(getCurrentUser());
propertyService.save(property);
return ResponseEntity.created(URI.create("/properties/" + property.getId())).build();
}
订单处理涉及状态机设计,核心状态转换如下:
java复制// 订单状态枚举
public enum OrderStatus {
PENDING_CONFIRMATION, // 待确认
CONFIRMED, // 已确认
CANCELLED, // 已取消
COMPLETED // 已完成
}
// 状态转换服务
@Service
@Transactional
public class OrderStateMachine {
public Order transition(Order order, OrderEvent event) {
switch (order.getStatus()) {
case PENDING_CONFIRMATION:
if (event == OrderEvent.CONFIRM) {
order.setStatus(OrderStatus.CONFIRMED);
} else if (event == OrderEvent.CANCEL) {
order.setStatus(OrderStatus.CANCELLED);
}
break;
case CONFIRMED:
if (event == OrderEvent.COMPLETE) {
order.setStatus(OrderStatus.COMPLETED);
}
break;
// 其他状态转换...
}
return orderRepository.save(order);
}
}
前端采用模块化组件设计,主要组件包括:
vue复制<template>
<div class="property-card">
<img :src="property.coverImage" class="property-image">
<div class="property-info">
<h3>{{ property.title }}</h3>
<div class="price">¥{{ property.pricePerNight }}/晚</div>
<div class="location">
<i class="el-icon-location"></i>
{{ property.location }}
</div>
<el-button type="primary" @click="viewDetail">查看详情</el-button>
</div>
</div>
</template>
<script>
export default {
props: {
property: {
type: Object,
required: true
}
},
methods: {
viewDetail() {
this.$router.push(`/properties/${this.property.id}`);
}
}
}
</script>
vue复制<template>
<el-form :model="form" :rules="rules" ref="bookingForm">
<el-form-item label="入住日期" prop="checkInDate">
<el-date-picker v-model="form.checkInDate" type="date"></el-date-picker>
</el-form-item>
<el-form-item label="退房日期" prop="checkOutDate">
<el-date-picker v-model="form.checkOutDate" type="date"></el-date-picker>
</el-form-item>
<el-button type="primary" @click="submitForm">提交订单</el-button>
</el-form>
</template>
<script>
export default {
data() {
return {
form: {
checkInDate: '',
checkOutDate: ''
},
rules: {
checkInDate: [{ required: true, message: '请选择入住日期', trigger: 'blur' }],
checkOutDate: [{ required: true, message: '请选择退房日期', trigger: 'blur' }]
}
};
},
methods: {
submitForm() {
this.$refs.bookingForm.validate(valid => {
if (valid) {
this.$emit('submit', this.form);
}
});
}
}
}
</script>
使用Vuex管理全局状态,特别是用户认证和购物车状态:
javascript复制// store/modules/auth.js
const state = {
user: null,
token: localStorage.getItem('token') || ''
};
const mutations = {
SET_USER(state, user) {
state.user = user;
},
SET_TOKEN(state, token) {
state.token = token;
localStorage.setItem('token', token);
},
LOGOUT(state) {
state.user = null;
state.token = '';
localStorage.removeItem('token');
}
};
const actions = {
login({ commit }, { username, password }) {
return AuthService.login(username, password)
.then(response => {
commit('SET_USER', response.data.user);
commit('SET_TOKEN', response.data.token);
return response;
});
},
logout({ commit }) {
commit('LOGOUT');
}
};
export default {
namespaced: true,
state,
mutations,
actions
};
Spring Boot应用打包为可执行JAR:
bash复制mvn clean package
java -jar target/rental-system-0.0.1-SNAPSHOT.jar
生产环境建议使用Docker部署:
dockerfile复制FROM openjdk:11-jre-slim
COPY target/rental-system-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
Vue项目构建:
bash复制npm run build
Nginx配置示例:
nginx复制server {
listen 80;
server_name your-domain.com;
location / {
root /path/to/dist;
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;
}
}
bash复制mysqldump -u username -p rental_system > backup.sql
这个项目从技术选型到最终上线花了约3个月时间,期间遇到了各种挑战,但也学到了很多实战经验。最大的体会是:好的系统设计比编码更重要,前期多花时间在架构设计上,后期能省去很多麻烦。