1. 项目概述
这个毕业设计项目是一个基于BS架构的小区家政服务预约平台,包含了完整的源码、文档和远程调试支持。BS架构(Browser/Server)意味着这是一个通过浏览器访问的Web应用,无需安装客户端软件,用户可以直接通过网页使用家政服务预约功能。
作为一个完整的毕业设计解决方案,该项目特别适合计算机相关专业的本科生作为毕业设计选题。它不仅涵盖了Web开发的完整技术栈,还涉及实际业务场景的解决方案,能够很好地展示学生的综合技术能力。
2. 核心功能设计
2.1 用户角色划分
系统主要包含三类用户角色:
-
业主用户:
- 注册/登录功能
- 浏览家政服务项目
- 预约服务(选择服务类型、时间、地点)
- 支付功能
- 评价已完成的服务
- 查看预约历史
-
家政服务人员:
- 服务人员注册/登录
- 服务项目管理
- 接单/拒单功能
- 服务进度更新
- 收入统计查看
-
管理员:
- 用户管理
- 服务项目管理
- 订单管理
- 数据统计分析
- 系统配置
2.2 核心业务流程
-
服务预约流程:
- 业主浏览服务项目 → 选择服务 → 选择时间 → 确认订单 → 支付 → 等待服务确认 → 服务完成 → 评价
-
服务接单流程:
- 服务人员查看新订单 → 接受/拒绝订单 → 服务准备 → 上门服务 → 服务完成确认 → 获得评价
-
后台管理流程:
- 管理员审核服务人员 → 管理服务项目 → 处理投诉 → 生成报表
3. 技术架构设计
3.1 前端技术选型
-
基础框架:
- HTML5 + CSS3:构建页面结构和样式
- JavaScript/ES6:实现页面交互逻辑
- Bootstrap:响应式布局框架,确保移动端适配
-
前端框架:
- Vue.js 2.x/3.x:渐进式JavaScript框架,构建用户界面
- Element UI/Vant:UI组件库,加速开发
-
辅助工具:
- Axios:处理HTTP请求
- ECharts:数据可视化展示
- Webpack:前端工程化构建工具
3.2 后端技术选型
-
核心框架:
- Spring Boot 2.x:简化Spring应用初始搭建和开发
- Spring Security:认证和授权框架
-
数据持久层:
- MyBatis/MyBatis-Plus:ORM框架
- MySQL 8.0:关系型数据库
- Redis:缓存服务
-
其他组件:
- Swagger:API文档生成
- Quartz:定时任务调度
- Lombok:简化Java代码
3.3 系统架构图
code复制┌───────────────────────────────────────────────────────┐
│ 客户端浏览器 │
└───────────────┬───────────────────┬───────────────────┘
│ │
┌───────────────▼───┐ ┌───────────▼───────────┐
│ 前端应用 │ │ 移动端适配 │
│ (V.js + Element) │ │ (响应式布局/小程序) │
└───────────────┬───┘ └───────────┬───────────┘
│ │
└─────────┬─────────┘
│
┌──────────▼──────────┐
│ API网关 │
│ (Spring Cloud Gateway) │
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ 业务微服务 │
│ (Spring Boot应用集群)│
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ 数据存储 │
│ (MySQL + Redis) │
└─────────────────────┘
4. 数据库设计
4.1 核心表结构
-
用户相关表:
user:用户基础信息user_role:用户角色关联service_provider:服务人员扩展信息
-
服务相关表:
service_type:服务类型service_item:服务项目provider_service:服务人员-服务关联
-
订单相关表:
order:订单主表order_detail:订单详情payment_record:支付记录
-
评价系统表:
review:评价记录complaint:投诉记录
4.2 关键表字段示例
用户表(user):
sql复制CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码(加密)',
`real_name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`phone` varchar(20) NOT NULL COMMENT '手机号',
`email` varchar(100) DEFAULT NULL COMMENT '邮箱',
`avatar` varchar(255) DEFAULT NULL COMMENT '头像URL',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '状态(0-禁用,1-正常)',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_username` (`username`),
UNIQUE KEY `idx_phone` (`phone`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
订单表(order):
sql复制CREATE TABLE `order` (
`id` bigint NOT NULL AUTO_INCREMENT,
`order_no` varchar(32) NOT NULL COMMENT '订单编号',
`user_id` bigint NOT NULL COMMENT '用户ID',
`provider_id` bigint DEFAULT NULL COMMENT '服务人员ID',
`service_id` bigint NOT NULL COMMENT '服务ID',
`order_status` tinyint NOT NULL DEFAULT '0' COMMENT '订单状态(0-待支付,1-待接单,2-已接单,3-服务中,4-已完成,5-已取消,6-已退款)',
`total_amount` decimal(10,2) NOT NULL COMMENT '订单总金额',
`pay_amount` decimal(10,2) NOT NULL COMMENT '实际支付金额',
`pay_time` datetime DEFAULT NULL COMMENT '支付时间',
`service_time` datetime NOT NULL COMMENT '预约服务时间',
`address` varchar(255) NOT NULL COMMENT '服务地址',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_order_no` (`order_no`),
KEY `idx_user_id` (`user_id`),
KEY `idx_provider_id` (`provider_id`),
KEY `idx_service_time` (`service_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单表';
5. 关键功能实现
5.1 服务预约功能实现
前端关键代码(Vue.js):
javascript复制// 服务预约组件
export default {
data() {
return {
serviceTypes: [], // 服务类型列表
services: [], // 服务项目列表
selectedService: null, // 选中的服务
selectedDate: '', // 选择的日期
timeSlots: [], // 可选时间段
selectedTime: '', // 选择的时间段
address: '', // 服务地址
remarks: '' // 备注
}
},
created() {
this.loadServiceTypes();
},
methods: {
async loadServiceTypes() {
try {
const res = await this.$api.get('/service/types');
this.serviceTypes = res.data;
} catch (error) {
this.$message.error('获取服务类型失败');
}
},
async loadServices(typeId) {
try {
const res = await this.$api.get('/service/list', { params: { typeId } });
this.services = res.data;
} catch (error) {
this.$message.error('获取服务项目失败');
}
},
async loadTimeSlots() {
if (!this.selectedService || !this.selectedDate) return;
try {
const res = await this.$api.get('/order/time-slots', {
params: {
serviceId: this.selectedService.id,
date: this.selectedDate
}
});
this.timeSlots = res.data;
} catch (error) {
this.$message.error('获取可用时间段失败');
}
},
async submitOrder() {
if (!this.validateForm()) return;
try {
const orderData = {
serviceId: this.selectedService.id,
serviceTime: `${this.selectedDate} ${this.selectedTime}`,
address: this.address,
remarks: this.remarks
};
const res = await this.$api.post('/order/create', orderData);
this.$router.push(`/order/pay/${res.data.orderNo}`);
} catch (error) {
this.$message.error('创建订单失败');
}
},
validateForm() {
// 验证逻辑...
}
}
}
后端关键代码(Spring Boot):
java复制@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/time-slots")
public Result<List<TimeSlotVO>> getAvailableTimeSlots(
@RequestParam Long serviceId,
@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
List<TimeSlotVO> timeSlots = orderService.getAvailableTimeSlots(serviceId, date);
return Result.success(timeSlots);
}
@PostMapping("/create")
public Result<OrderCreateVO> createOrder(@RequestBody OrderCreateDTO dto,
@CurrentUser LoginUser user) {
OrderCreateVO vo = orderService.createOrder(dto, user.getId());
return Result.success(vo);
}
}
@Service
@Transactional
public class OrderServiceImpl implements OrderService {
@Override
public List<TimeSlotVO> getAvailableTimeSlots(Long serviceId, LocalDate date) {
// 1. 获取服务的基本信息
ServiceItem service = serviceItemMapper.selectById(serviceId);
if (service == null) {
throw new BusinessException("服务不存在");
}
// 2. 获取服务人员的排班信息
List<ProviderSchedule> schedules = providerScheduleMapper.selectByServiceAndDate(
serviceId, date);
// 3. 获取已预约的时间段
LocalDateTime startTime = date.atStartOfDay();
LocalDateTime endTime = date.plusDays(1).atStartOfDay();
List<Order> orders = orderMapper.selectByServiceAndTimeRange(
serviceId, startTime, endTime);
// 4. 生成可用时间段
return generateAvailableTimeSlots(service, schedules, orders);
}
@Override
public OrderCreateVO createOrder(OrderCreateDTO dto, Long userId) {
// 1. 验证服务信息
ServiceItem service = validateService(dto.getServiceId());
// 2. 验证时间是否可用
validateTimeSlot(dto.getServiceId(), dto.getServiceTime());
// 3. 创建订单
Order order = new Order();
order.setOrderNo(generateOrderNo());
order.setUserId(userId);
order.setServiceId(dto.getServiceId());
order.setOrderStatus(OrderStatus.WAIT_PAYMENT.getCode());
order.setTotalAmount(service.getPrice());
order.setPayAmount(service.getPrice());
order.setServiceTime(dto.getServiceTime());
order.setAddress(dto.getAddress());
order.setRemarks(dto.getRemarks());
orderMapper.insert(order);
// 4. 返回订单创建结果
OrderCreateVO vo = new OrderCreateVO();
vo.setOrderNo(order.getOrderNo());
vo.setAmount(order.getPayAmount());
return vo;
}
// 其他辅助方法...
}
5.2 服务分配算法
家政服务平台的特色功能是智能分配服务人员,考虑以下因素:
- 服务人员的技能匹配度
- 地理位置就近原则
- 服务人员的当前负载
- 用户评价和历史合作情况
分配算法实现:
java复制public class ProviderAllocationAlgorithm {
public static ProviderScore evaluateProvider(Order order, ServiceProvider provider) {
ProviderScore score = new ProviderScore();
// 1. 技能匹配度 (权重40%)
score.setSkillScore(calculateSkillMatchScore(order, provider));
// 2. 距离分数 (权重30%)
score.setDistanceScore(calculateDistanceScore(order, provider));
// 3. 当前负载 (权重20%)
score.setLoadScore(calculateLoadScore(provider));
// 4. 历史评价 (权重10%)
score.setReviewScore(calculateReviewScore(provider));
// 计算综合得分
score.calculateTotalScore();
return score;
}
private static double calculateSkillMatchScore(Order order, ServiceProvider provider) {
// 获取服务需要的技能
ServiceItem service = serviceItemMapper.selectById(order.getServiceId());
Set<Long> requiredSkills = service.getRequiredSkillIds();
// 获取服务人员具备的技能
Set<Long> providerSkills = providerSkillMapper.selectByProviderId(provider.getId())
.stream()
.map(ProviderSkill::getSkillId)
.collect(Collectors.toSet());
// 计算匹配度
long matchedCount = requiredSkills.stream()
.filter(providerSkills::contains)
.count();
return (double) matchedCount / requiredSkills.size();
}
private static double calculateDistanceScore(Order order, ServiceProvider provider) {
// 使用地理位置API计算距离
double distance = LocationUtils.calculateDistance(
order.getAddress().getCoordinate(),
provider.getAddress().getCoordinate());
// 距离越近分数越高
return 1 / (1 + distance);
}
private static double calculateLoadScore(ServiceProvider provider) {
// 获取服务人员当前的任务数
int currentTasks = orderMapper.countByProviderAndStatus(
provider.getId(),
Arrays.asList(OrderStatus.ACCEPTED.getCode(), OrderStatus.IN_PROGRESS.getCode()));
// 计算负载率
double loadRatio = (double) currentTasks / provider.getMaxConcurrentTasks();
// 负载越低分数越高
return 1 - loadRatio;
}
private static double calculateReviewScore(ServiceProvider provider) {
// 获取服务人员的平均评分
Double avgRating = reviewMapper.selectAvgRatingByProvider(provider.getId());
if (avgRating == null) {
return 0.8; // 默认值
}
// 线性映射到0-1范围 (假设评分是1-5星)
return (avgRating - 1) / 4;
}
}
6. 系统部署与远程调试
6.1 开发环境搭建
-
前端环境:
- Node.js 14+
- Vue CLI 4+
- 推荐IDE:VSCode或WebStorm
-
后端环境:
- JDK 1.8+
- Maven 3.6+
- MySQL 8.0
- Redis 5+
- 推荐IDE:IntelliJ IDEA
6.2 项目启动步骤
-
数据库初始化:
sql复制CREATE DATABASE IF NOT EXISTS home_service DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_general_ci; USE home_service; SOURCE /path/to/sql/file.sql; -
后端启动:
bash复制# 克隆项目 git clone https://github.com/example/home-service.git cd home-service/backend # 修改配置文件 vim src/main/resources/application-dev.yml # 编译并运行 mvn clean package java -jar target/home-service.jar -
前端启动:
bash复制cd ../frontend # 安装依赖 npm install # 修改配置 vim .env.development # 启动开发服务器 npm run serve
6.3 远程调试配置
后端远程调试:
-
修改启动脚本,添加JVM参数:
bash复制java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar target/home-service.jar -
在IDE中创建Remote JVM Debug配置:
- Host: 服务器IP
- Port: 5005
- 选择"Attach to remote JVM"
前端远程调试:
-
配置vue.config.js:
javascript复制module.exports = { devServer: { host: '0.0.0.0', port: 8080, proxy: { '/api': { target: 'http://backend-server:8080', changeOrigin: true } } } } -
使用Chrome开发者工具进行调试:
- 打开Chrome开发者工具(F12)
- 切换到Sources或Console标签页
- 可以设置断点、查看网络请求等
7. 项目文档结构
完整的毕业设计文档应包含以下部分:
code复制├── 文档
│ ├── 开题报告.docx
│ ├── 需求规格说明书.docx
│ ├── 系统设计说明书.docx
│ ├── 数据库设计说明书.docx
│ ├── 用户手册.docx
│ └── 毕业论文.docx
├── 源码
│ ├── backend
│ │ ├── src
│ │ ├── pom.xml
│ │ └── ...
│ └── frontend
│ ├── public
│ ├── src
│ ├── package.json
│ └── ...
├── 数据库脚本
│ ├── schema.sql
│ └── data.sql
└── 其他材料
├── 答辩PPT.pptx
└── 演示视频.mp4
8. 常见问题与解决方案
8.1 开发阶段问题
-
跨域问题:
- 现象:前端请求后端API时出现CORS错误
- 解决方案:
java复制@Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("*") .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") .allowedHeaders("*") .maxAge(3600); } }
-
时区问题:
- 现象:数据库中的时间与Java程序中的时间不一致
- 解决方案:
- 在JDBC连接字符串中添加时区参数:
yaml复制spring: datasource: url: jdbc:mysql://localhost:3306/home_service?useSSL=false&serverTimezone=Asia/Shanghai - 在Spring Boot主类中添加时区设置:
java复制@PostConstruct void started() { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); }
- 在JDBC连接字符串中添加时区参数:
8.2 部署阶段问题
-
端口冲突:
- 现象:应用启动失败,报端口被占用
- 解决方案:
- 查找占用端口的进程:
bash复制
netstat -tunlp | grep 8080 - 终止占用进程或修改应用端口:
yaml复制server: port: 8081
- 查找占用端口的进程:
-
数据库连接失败:
- 现象:应用启动时报数据库连接异常
- 解决方案:
- 检查数据库服务是否启动
- 检查连接字符串、用户名和密码是否正确
- 检查数据库用户是否有远程连接权限
8.3 业务逻辑问题
- 并发预约问题:
- 现象:同一时间段可能被多个用户预约
- 解决方案:
- 使用数据库乐观锁:
java复制@Transactional public boolean createOrderWithLock(OrderCreateDTO dto) { // 1. 查询服务当前状态 ServiceItem service = serviceItemMapper.selectById(dto.getServiceId()); // 2. 检查时间是否可用 if (!isTimeSlotAvailable(dto.getServiceId(), dto.getServiceTime())) { return false; } // 3. 创建订单 Order order = createOrderEntity(dto); orderMapper.insert(order); // 4. 更新服务时间状态 int rows = serviceItemMapper.updateTimeSlotStatus( dto.getServiceId(), dto.getServiceTime(), TimeSlotStatus.BOOKED.getCode()); return rows > 0; } - 使用Redis分布式锁:
java复制public boolean createOrderWithRedisLock(OrderCreateDTO dto) { String lockKey = "lock:service:" + dto.getServiceId() + ":" + dto.getServiceTime(); String requestId = UUID.randomUUID().toString(); try { // 尝试获取锁 boolean locked = redisTemplate.opsForValue().setIfAbsent( lockKey, requestId, 30, TimeUnit.SECONDS); if (!locked) { return false; } // 执行业务逻辑 return createOrder(dto); } finally { // 释放锁 if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) { redisTemplate.delete(lockKey); } } }
- 使用数据库乐观锁:
9. 项目扩展方向
-
移动端适配:
- 开发微信小程序版本
- 开发React Native跨平台App
-
智能推荐系统:
- 基于用户历史行为推荐服务
- 基于季节、节假日的服务推荐
-
支付系统增强:
- 接入更多支付渠道
- 实现分期付款功能
-
数据分析平台:
- 用户行为分析
- 服务需求预测
- 服务人员绩效评估
-
物联网集成:
- 智能门锁对接
- 服务过程监控
这个基于BS架构的小区家政服务预约平台项目涵盖了现代Web开发的完整技术栈,从前端到后端,从数据库设计到系统部署,是一个非常适合作为计算机相关专业毕业设计的选题。它不仅能够帮助学生巩固所学知识,还能让学生接触到真实的业务场景和开发流程。
