基于 Spring Boot + JPA + MySQL的上门家政系统代码示例

​一个基于 Spring Boot + JPA + MySQL 的上门家政系统核心功能示例代码,涵盖了用户、服务人员、服务项目、订单管理的典型实现。

项目结构

css 复制代码
src/main/java/com/housekeeping/
├── entity/
│   ├── User.java
│   ├── Staff.java
│   ├── ServiceItem.java
│   ├── Order.java
│   └── OrderStatus.java
├── repository/
│   ├── UserRepository.java
│   ├── StaffRepository.java
│   ├── ServiceItemRepository.java
│   └── OrderRepository.java
├── service/
│   ├── OrderService.java
│   └── UserService.java
├── controller/
│   ├── OrderController.java
│   └── UserController.java
├── dto/
│   ├── OrderCreateDTO.java
│   └── OrderResponseDTO.java
└── HousekeepingApplication.java

1. Maven 依赖(pom.xml 核心部分)

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. 实体类

2.1 用户实体(User.java)

kotlin 复制代码
package com.housekeeping.entity;

import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "user")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String phone;
    private String address;
    private LocalDateTime createTime;
}

2.2 家政人员实体(Staff.java)

kotlin 复制代码
package com.housekeeping.entity;

import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "staff")
public class Staff {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String phone;
    private String skill;        // 擅长服务类型
    private Double rating;       // 平均评分
    private Boolean available;   // 是否可接单
    private LocalDateTime createTime;
}

2.3 服务项目实体(ServiceItem.java)

kotlin 复制代码
package com.housekeeping.entity;

import lombok.Data;
import javax.persistence.*;

@Data
@Entity
@Table(name = "service_item")
public class ServiceItem {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;         // 如:日常保洁、深度保洁
    private String description;
    private Double pricePerHour; // 每小时价格
}

2.4 订单状态枚举(OrderStatus.java)

java 复制代码
package com.housekeeping.entity;

public enum OrderStatus {
    PENDING,      // 待确认
    CONFIRMED,    // 已确认
    PROCESSING,   // 服务中
    COMPLETED,    // 已完成
    CANCELLED     // 已取消
}

2.5 订单实体(Order.java)

kotlin 复制代码
package com.housekeeping.entity;

import lombok.Data;
import javax.persistence.*;
import java.time.LocalDateTime;

@Data
@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private Long userId;

    @Column(nullable = false)
    private Long staffId;

    @Column(nullable = false)
    private Long serviceItemId;

    private LocalDateTime serviceStartTime;
    private Integer durationHours;     // 服务时长(小时)
    private Double totalPrice;
    private String address;

    @Enumerated(EnumType.STRING)
    private OrderStatus status;

    private String comment;            // 评价内容
    private Integer rating;            // 评分1-5

    private LocalDateTime createTime;
    private LocalDateTime updateTime;
}

3. Repository 层

less 复制代码
package com.housekeeping.repository;

import com.housekeeping.entity.Order;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.time.LocalDateTime;
import java.util.List;

public interface OrderRepository extends JpaRepository<Order, Long> {
    List<Order> findByUserId(Long userId);
    List<Order> findByStaffId(Long staffId);

    // 查询某家政人员在指定时间段是否有重叠订单
    @Query("SELECT COUNT(o) FROM Order o WHERE o.staffId = :staffId " +
           "AND o.status != 'CANCELLED' " +
           "AND o.serviceStartTime < :endTime " +
           "AND (o.serviceStartTime + (o.durationHours || ' hour') ) > :startTime")
    int countOverlappingOrders(@Param("staffId") Long staffId,
                               @Param("startTime") LocalDateTime startTime,
                               @Param("endTime") LocalDateTime endTime);
}

注意:上述 JPA 时间运算依赖数据库方言,实际开发中更推荐使用原生查询或改用 Java 逻辑。此处为演示冲突检测思路。

4. DTO 类

kotlin 复制代码
package com.housekeeping.dto;

import lombok.Data;
import java.time.LocalDateTime;

@Data
public class OrderCreateDTO {
    private Long userId;
    private Long staffId;
    private Long serviceItemId;
    private LocalDateTime serviceStartTime;
    private Integer durationHours;
    private String address;
}
kotlin 复制代码
package com.housekeeping.dto;

import lombok.Data;
import com.housekeeping.entity.OrderStatus;
import java.time.LocalDateTime;

@Data
public class OrderResponseDTO {
    private Long id;
    private Long userId;
    private Long staffId;
    private Long serviceItemId;
    private LocalDateTime serviceStartTime;
    private Integer durationHours;
    private Double totalPrice;
    private String address;
    private OrderStatus status;
    private LocalDateTime createTime;
}

5. 服务层

5.1 订单服务(OrderService.java)

scss 复制代码
package com.housekeeping.service;

import com.housekeeping.dto.OrderCreateDTO;
import com.housekeeping.dto.OrderResponseDTO;
import com.housekeeping.entity.*;
import com.housekeeping.repository.*;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderRepository orderRepository;
    private final UserRepository userRepository;
    private final StaffRepository staffRepository;
    private final ServiceItemRepository serviceItemRepository;

    @Transactional
    public OrderResponseDTO createOrder(OrderCreateDTO dto) {
        // 校验用户、家政人员、服务项目是否存在
        User user = userRepository.findById(dto.getUserId())
                .orElseThrow(() -> new RuntimeException("用户不存在"));
        Staff staff = staffRepository.findById(dto.getStaffId())
                .orElseThrow(() -> new RuntimeException("家政人员不存在"));
        ServiceItem item = serviceItemRepository.findById(dto.getServiceItemId())
                .orElseThrow(() -> new RuntimeException("服务项目不存在"));

        if (!staff.getAvailable()) {
            throw new RuntimeException("该家政人员当前不可接单");
        }

        // 时间冲突检测(简化:通过Java检查重叠)
        LocalDateTime start = dto.getServiceStartTime();
        LocalDateTime end = start.plusHours(dto.getDurationHours());
        boolean conflict = orderRepository.findAll().stream()
                .filter(o -> o.getStaffId().equals(staff.getId()))
                .filter(o -> o.getStatus() != OrderStatus.CANCELLED)
                .anyMatch(o -> {
                    LocalDateTime oStart = o.getServiceStartTime();
                    LocalDateTime oEnd = oStart.plusHours(o.getDurationHours());
                    return start.isBefore(oEnd) && end.isAfter(oStart);
                });
        if (conflict) {
            throw new RuntimeException("该时间段已有其他订单,请重新选择时间");
        }

        // 计算总价
        Double totalPrice = item.getPricePerHour() * dto.getDurationHours();

        Order order = new Order();
        order.setUserId(user.getId());
        order.setStaffId(staff.getId());
        order.setServiceItemId(item.getId());
        order.setServiceStartTime(start);
        order.setDurationHours(dto.getDurationHours());
        order.setTotalPrice(totalPrice);
        order.setAddress(dto.getAddress());
        order.setStatus(OrderStatus.PENDING);
        order.setCreateTime(LocalDateTime.now());
        order.setUpdateTime(LocalDateTime.now());

        Order saved = orderRepository.save(order);
        return convertToDTO(saved);
    }

    @Transactional
    public void cancelOrder(Long orderId, Long userId) {
        Order order = orderRepository.findById(orderId)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
        if (!order.getUserId().equals(userId)) {
            throw new RuntimeException("无权操作此订单");
        }
        if (order.getStatus() != OrderStatus.PENDING) {
            throw new RuntimeException("只有待确认的订单才能取消");
        }
        order.setStatus(OrderStatus.CANCELLED);
        order.setUpdateTime(LocalDateTime.now());
        orderRepository.save(order);
    }

    @Transactional
    public void completeOrder(Long orderId, Long staffId, String comment, Integer rating) {
        Order order = orderRepository.findById(orderId)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
        if (!order.getStaffId().equals(staffId)) {
            throw new RuntimeException("无权操作此订单");
        }
        if (order.getStatus() != OrderStatus.PROCESSING) {
            throw new RuntimeException("只有服务中的订单才能完成");
        }
        order.setStatus(OrderStatus.COMPLETED);
        order.setComment(comment);
        order.setRating(rating);
        order.setUpdateTime(LocalDateTime.now());
        orderRepository.save(order);
    }

    private OrderResponseDTO convertToDTO(Order order) {
        OrderResponseDTO dto = new OrderResponseDTO();
        dto.setId(order.getId());
        dto.setUserId(order.getUserId());
        dto.setStaffId(order.getStaffId());
        dto.setServiceItemId(order.getServiceItemId());
        dto.setServiceStartTime(order.getServiceStartTime());
        dto.setDurationHours(order.getDurationHours());
        dto.setTotalPrice(order.getTotalPrice());
        dto.setAddress(order.getAddress());
        dto.setStatus(order.getStatus());
        dto.setCreateTime(order.getCreateTime());
        return dto;
    }
}

6. 控制器层

6.1 订单控制器(OrderController.java)

less 复制代码
package com.housekeeping.controller;

import com.housekeeping.dto.OrderCreateDTO;
import com.housekeeping.dto.OrderResponseDTO;
import com.housekeeping.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/orders")
@RequiredArgsConstructor
public class OrderController {

    private final OrderService orderService;

    @PostMapping("/create")
    public ResponseEntity<?> createOrder(@RequestBody OrderCreateDTO dto) {
        try {
            OrderResponseDTO order = orderService.createOrder(dto);
            return ResponseEntity.ok(order);
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }

    @PutMapping("/cancel/{orderId}")
    public ResponseEntity<?> cancelOrder(@PathVariable Long orderId, @RequestParam Long userId) {
        try {
            orderService.cancelOrder(orderId, userId);
            return ResponseEntity.ok("订单已取消");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }

    @PutMapping("/complete/{orderId}")
    public ResponseEntity<?> completeOrder(@PathVariable Long orderId,
                                           @RequestParam Long staffId,
                                           @RequestParam String comment,
                                           @RequestParam Integer rating) {
        try {
            orderService.completeOrder(orderId, staffId, comment, rating);
            return ResponseEntity.ok("订单已完成并评价");
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(e.getMessage());
        }
    }
}

6.2 用户控制器示例(UserController.java)

kotlin 复制代码
package com.housekeeping.controller;

import com.housekeeping.entity.Order;
import com.housekeeping.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/api/users")
@RequiredArgsConstructor
public class UserController {

    private final OrderRepository orderRepository;

    @GetMapping("/{userId}/orders")
    public List<Order> getUserOrders(@PathVariable Long userId) {
        return orderRepository.findByUserId(userId);
    }
}

7. 配置文件(application.properties)

ini 复制代码
spring.datasource.url=jdbc:mysql://localhost:3306/housekeeping?useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

8. 主启动类

typescript 复制代码
package com.housekeeping;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HousekeepingApplication {
    public static void main(String[] args) {
        SpringApplication.run(HousekeepingApplication.class, args);
    }
}

9. 初始化测试数据(可选 CommandLineRunner)

java 复制代码
@Component
@RequiredArgsConstructor
public class DataInitializer implements CommandLineRunner {
    private final UserRepository userRepository;
    private final StaffRepository staffRepository;
    private final ServiceItemRepository serviceItemRepository;

    @Override
    public void run(String... args) throws Exception {
        if (userRepository.count() == 0) {
            User user = new User();
            user.setName("张三");
            user.setPhone("13800000000");
            user.setAddress("北京市朝阳区xxx");
            user.setCreateTime(LocalDateTime.now());
            userRepository.save(user);

            Staff staff = new Staff();
            staff.setName("李阿姨");
            staff.setPhone("13911111111");
            staff.setSkill("日常保洁、深度保洁");
            staff.setRating(4.9);
            staff.setAvailable(true);
            staff.setCreateTime(LocalDateTime.now());
            staffRepository.save(staff);

            ServiceItem item = new ServiceItem();
            item.setName("日常保洁");
            item.setDescription("全屋基础清洁");
            item.setPricePerHour(50.0);
            serviceItemRepository.save(item);
        }
    }
}

核心业务说明

  1. 下单流程:用户选择服务项目、家政人员、上门时间 → 系统校验人员可用性及时间冲突 → 自动计费 → 生成待确认订单。
  2. 时间冲突检测:通过遍历该人员的未取消订单,判断时间区间是否有重叠。
  3. 订单状态流转:PENDING(待确认)→ CONFIRMED(员工确认后)→ PROCESSING(开始服务)→ COMPLETED(完成+评价),或直接 CANCELLED。
  4. 评价体系:订单完成后可填写文字评价和1-5星评分,后续用于展示家政人员平均分。

此示例可直接扩展为完整项目,加入登录认证 (JWT/Spring Security)、支付集成服务人员自动分配位置距离排序等高级功能。

相关推荐
爱喝铁观音的谷力景辉1 小时前
web端实现音频波形分析以及音频截取
前端
该用户已不存在1 小时前
别再把 Claude 当聊天框,Claude Code CLI 安装与上下文管理指北(Part 1)
后端·ai编程·claude
前端那点事1 小时前
别再乱用Vue3路由!useRoute/useRouter传参、跳转、避坑最全实战指南
前端
蝎子莱莱爱打怪1 小时前
无废话!源自官网的Codex 命令速查手册!
人工智能·后端·agent
盖世英雄酱581361 小时前
6000条数据执行时间9s(二)
数据库·后端
程序员老邢1 小时前
【技术底稿 32】Nginx 经典大坑复盘:本机公网域名自环代理,导致接口返回首页 / 404 实战排障
java·运维·nginx·前后端分离·技术底稿·后端部署
LIO2 小时前
深度解析 localStorage 与 sessionStorage:用法、区别与最佳实践
前端
Amy_yang2 小时前
uni-app 中 web-view 的使用与 App 端全屏问题处理
前端·javascript·vue.js