作为一名长期深耕Java Web开发的技术博主,今天想和大家分享一个我最近完成的实战项目——基于SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0的家政服务平台系统。这个项目源于我观察到身边很多朋友在家政服务过程中遇到的痛点:信息不对称、服务流程不透明、支付安全没保障等问题。
这个系统采用前后端分离架构,后端使用SpringBoot2框架,前端采用Vue3+ElementUI,数据库选用MySQL8.0,并整合了Redis缓存和JWT安全认证。系统实现了用户管理、服务分类展示、在线预约、订单跟踪、支付结算和评价反馈等核心功能模块,支持用户、家政人员和管理员三种角色。
后端采用SpringBoot2作为基础框架,主要基于以下考虑:
数据库访问层选用MyBatis-Plus而非原生MyBatis,主要看中其:
java复制// MyBatis-Plus示例代码
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
public Page<User> getUsersByPage(int pageNum, int pageSize) {
return page(new Page<>(pageNum, pageSize),
Wrappers.<User>lambdaQuery().orderByAsc(User::getRegisterTime));
}
}
前端采用Vue3+ElementUI组合,主要优势在于:
javascript复制// Vue3组件示例
<template>
<el-table :data="orderList" style="width: 100%">
<el-table-column prop="orderId" label="订单号" width="180" />
<el-table-column prop="serviceType" label="服务类型" />
<el-table-column prop="orderStatus" label="状态">
<template #default="{row}">
<el-tag :type="statusTagType(row.orderStatus)">
{{ statusText(row.orderStatus) }}
</el-tag>
</template>
</el-table-column>
</el-table>
</template>
系统采用典型的前后端分离架构:
code复制客户端层:Web浏览器、移动端H5
↓
表现层:Vue3前端应用
↓ (RESTful API)
业务逻辑层:SpringBoot应用
↓
数据访问层:MyBatis-Plus
↓
数据存储层:MySQL8.0 + Redis缓存
关键设计决策:
系统采用JWT实现安全认证,关键流程如下:
注意:JWT的secret密钥需要足够复杂,建议使用UUID生成并定期更换
java复制// JWT工具类核心代码
public class JwtUtil {
private static final String SECRET = "your-256-bit-secret";
private static final long EXPIRATION = 86400000; // 24小时
public static String generateToken(UserDetails user) {
return Jwts.builder()
.setSubject(user.getUsername())
.claim("roles", user.getAuthorities())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS256, SECRET)
.compact();
}
public static UserDetails parseToken(String token) {
Claims claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
// 解析claims构建UserDetails
}
}
订单状态流转是系统的核心业务逻辑,我们采用状态模式实现:
code复制待接单 → (家政人员接单) → 进行中
→ (用户取消) → 已取消
进行中 → (服务完成) → 待评价
待评价 → (用户评价) → 已完成
状态转换的约束条件:
java复制// 订单状态枚举
public enum OrderStatus {
PENDING(1, "待接单"),
ACCEPTED(2, "进行中"),
COMPLETED(3, "已完成"),
CANCELLED(4, "已取消"),
TO_BE_REVIEWED(5, "待评价");
// 状态转换校验逻辑
public boolean canTransferTo(OrderStatus newStatus) {
switch(this) {
case PENDING:
return newStatus == ACCEPTED || newStatus == CANCELLED;
case ACCEPTED:
return newStatus == COMPLETED;
// 其他状态转换规则...
}
}
}
支付模块采用支付宝沙箱环境实现,关键流程:
重要:支付结果必须通过异步通知确认,不能依赖前端回调
java复制// 支付服务核心代码
@Service
public class PaymentServiceImpl implements PaymentService {
@Async
public void handleAlipayNotify(Map<String, String> params) {
// 1. 验证签名
boolean signVerified = AlipaySignature.rsaCheckV1(params, ALIPAY_PUBLIC_KEY, "UTF-8", "RSA2");
// 2. 验证订单金额等信息
String orderId = params.get("out_trade_no");
Order order = orderService.getById(orderId);
if(order.getTotalPrice().compareTo(new BigDecimal(params.get("total_amount"))) != 0) {
throw new RuntimeException("金额不一致");
}
// 3. 更新订单状态
if("TRADE_SUCCESS".equals(params.get("trade_status"))) {
order.setPayStatus(PayStatus.PAID);
orderService.updateById(order);
}
}
}
系统主要包含以下核心表:
sql复制-- 用户表示例DDL
CREATE TABLE `user` (
`user_id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`phone` varchar(20) NOT NULL,
`email` varchar(50) DEFAULT NULL,
`user_type` tinyint NOT NULL COMMENT '1-用户,2-家政人员,3-管理员',
`register_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`last_login_time` datetime DEFAULT NULL,
PRIMARY KEY (`user_id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
针对查询性能优化,我们设计了以下索引:
sql复制-- 订单表索引示例
ALTER TABLE `service_order`
ADD INDEX `idx_user` (`user_id`),
ADD INDEX `idx_provider` (`service_provider`),
ADD INDEX `idx_status_time` (`order_status`, `order_time`);
随着业务增长,我们预留了以下扩展方案:
推荐的生产环境部署架构:
code复制前端服务:Nginx静态资源服务 + CDN加速
↑
API网关:Spring Cloud Gateway
↑
微服务集群:
- 用户服务
- 订单服务
- 支付服务
↑
数据层:
- MySQL主从集群
- Redis哨兵集群
- 文件存储(OSS)
使用Redis缓存以下数据:
java复制// Redis缓存示例
@Service
public class UserCacheServiceImpl implements UserCacheService {
private final RedisTemplate<String, Object> redisTemplate;
public User getUserById(Long userId) {
String key = "user:" + userId;
User user = (User) redisTemplate.opsForValue().get(key);
if(user == null) {
user = userMapper.selectById(userId);
if(user != null) {
redisTemplate.opsForValue().set(key, user, 30, TimeUnit.MINUTES);
}
}
return user;
}
}
建议集成以下监控组件:
问题现象:前端获取的JWT令牌在有效期内突然失效
排查步骤:
解决方案:
问题场景:支付成功但订单状态未更新
可能原因:
解决方案:
java复制@Transactional
public void handlePaySuccess(String orderId) {
// 使用select for update加锁
Order order = orderMapper.selectByIdForUpdate(orderId);
if(order.getPayStatus() == PayStatus.UNPAID) {
order.setPayStatus(PayStatus.PAID);
orderMapper.updateById(order);
// 记录支付日志
payLogService.recordPaySuccess(order);
}
}
性能瓶颈:促销活动时下单接口响应变慢
优化措施:
java复制public boolean createOrder(OrderDTO orderDTO) {
// 1. Redis原子扣减
Long remain = redisTemplate.opsForValue()
.decrement("inventory:" + orderDTO.getServiceType());
if(remain < 0) {
// 回滚
redisTemplate.opsForValue()
.increment("inventory:" + orderDTO.getServiceType());
return false;
}
// 2. 异步处理订单
orderAsyncService.asyncCreateOrder(orderDTO);
return true;
}
在实际开发过程中,我们还遇到了Vue3组件复用导致的props污染问题,最终通过给每个组件实例分配唯一key解决。MySQL8.0的窗口函数在分页查询时性能表现优异,比传统limit方式快了近3倍。