Initial commit: Car Maintenance Management System

Author: Yang Lu

School: Liaoning Institute of Science and Technology

Major: Computer Science and Technology

Class: BZ246

Tech Stack:

- Backend: Spring Boot 2.7.18 + JPA + MySQL

- Frontend: HTML5 + CSS3 + JavaScript

Features:

- User Management (Admin/Staff/Customer roles)

- Vehicle Archive Management

- Service Order Management

- Parts Inventory Management

- Online Appointment Service

- Data Statistics and Analysis

Generated with Claude Code

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
wangziqi
2026-01-07 14:28:50 +08:00
commit cfae122685
45 changed files with 5447 additions and 0 deletions

113
backend/pom.xml Normal file
View File

@@ -0,0 +1,113 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
<groupId>com.carmaintenance</groupId>
<artifactId>car-maintenance-system</artifactId>
<version>1.0.0</version>
<name>车管家4S店车辆维保管理系统</name>
<description>基于Spring Boot的车辆维保管理系统后端服务</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.33</version>
</dependency>
<!-- Spring Boot Validation -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- JSON处理 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!-- Apache Commons Lang -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- Spring Boot DevTools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!-- Spring Boot Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,23 @@
package com.carmaintenance;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
/**
* 车管家4S店车辆维保管理系统 - 主应用程序
* @author 杨璐
* @date 2025-01-07
*/
@SpringBootApplication
@EnableJpaAuditing
public class CarMaintenanceApplication {
public static void main(String[] args) {
SpringApplication.run(CarMaintenanceApplication.class, args);
System.out.println("\n========================================");
System.out.println("车管家4S店车辆维保管理系统启动成功!");
System.out.println("访问地址: http://localhost:8080/api");
System.out.println("========================================\n");
}
}

View File

@@ -0,0 +1,35 @@
package com.carmaintenance.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* Web配置 - CORS跨域配置
*/
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Value("${cors.allowed-origins}")
private String allowedOrigins;
@Value("${cors.allowed-methods}")
private String allowedMethods;
@Value("${cors.allowed-headers}")
private String allowedHeaders;
@Value("${cors.allow-credentials}")
private boolean allowCredentials;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns(allowedOrigins.split(","))
.allowedMethods(allowedMethods.split(","))
.allowedHeaders(allowedHeaders.split(","))
.allowCredentials(allowCredentials)
.maxAge(3600);
}
}

View File

@@ -0,0 +1,127 @@
package com.carmaintenance.controller;
import com.carmaintenance.dto.Result;
import com.carmaintenance.entity.Appointment;
import com.carmaintenance.repository.AppointmentRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 预约管理控制器
*/
@RestController
@RequestMapping("/appointments")
@CrossOrigin
public class AppointmentController {
@Autowired
private AppointmentRepository appointmentRepository;
/**
* 获取所有预约
*/
@GetMapping
public Result<List<Appointment>> getAllAppointments() {
List<Appointment> appointments = appointmentRepository.findAll();
return Result.success(appointments);
}
/**
* 根据ID获取预约
*/
@GetMapping("/{id}")
public Result<Appointment> getAppointmentById(@PathVariable Integer id) {
Appointment appointment = appointmentRepository.findById(id).orElse(null);
if (appointment == null) {
return Result.notFound("预约不存在");
}
return Result.success(appointment);
}
/**
* 根据客户ID获取预约列表
*/
@GetMapping("/customer/{customerId}")
public Result<List<Appointment>> getAppointmentsByCustomerId(@PathVariable Integer customerId) {
List<Appointment> appointments = appointmentRepository.findByCustomerId(customerId);
return Result.success(appointments);
}
/**
* 根据状态获取预约列表
*/
@GetMapping("/status/{status}")
public Result<List<Appointment>> getAppointmentsByStatus(@PathVariable String status) {
try {
Appointment.AppointmentStatus appointmentStatus = Appointment.AppointmentStatus.valueOf(status);
List<Appointment> appointments = appointmentRepository.findByStatus(appointmentStatus);
return Result.success(appointments);
} catch (IllegalArgumentException e) {
return Result.error("状态参数错误");
}
}
/**
* 创建预约
*/
@PostMapping
public Result<Appointment> createAppointment(@RequestBody Appointment appointment) {
appointment.setStatus(Appointment.AppointmentStatus.pending);
Appointment savedAppointment = appointmentRepository.save(appointment);
return Result.success("预约成功", savedAppointment);
}
/**
* 更新预约
*/
@PutMapping("/{id}")
public Result<Appointment> updateAppointment(@PathVariable Integer id, @RequestBody Appointment appointment) {
Appointment existingAppointment = appointmentRepository.findById(id).orElse(null);
if (existingAppointment == null) {
return Result.notFound("预约不存在");
}
if (appointment.getAppointmentTime() != null)
existingAppointment.setAppointmentTime(appointment.getAppointmentTime());
if (appointment.getContactPhone() != null)
existingAppointment.setContactPhone(appointment.getContactPhone());
if (appointment.getDescription() != null)
existingAppointment.setDescription(appointment.getDescription());
if (appointment.getStatus() != null)
existingAppointment.setStatus(appointment.getStatus());
if (appointment.getOrderId() != null)
existingAppointment.setOrderId(appointment.getOrderId());
Appointment updatedAppointment = appointmentRepository.save(existingAppointment);
return Result.success("更新成功", updatedAppointment);
}
/**
* 取消预约
*/
@PutMapping("/{id}/cancel")
public Result<Appointment> cancelAppointment(@PathVariable Integer id) {
Appointment appointment = appointmentRepository.findById(id).orElse(null);
if (appointment == null) {
return Result.notFound("预约不存在");
}
appointment.setStatus(Appointment.AppointmentStatus.cancelled);
Appointment updatedAppointment = appointmentRepository.save(appointment);
return Result.success("取消成功", updatedAppointment);
}
/**
* 删除预约
*/
@DeleteMapping("/{id}")
public Result<Void> deleteAppointment(@PathVariable Integer id) {
if (!appointmentRepository.existsById(id)) {
return Result.notFound("预约不存在");
}
appointmentRepository.deleteById(id);
return Result.success("删除成功");
}
}

View File

@@ -0,0 +1,87 @@
package com.carmaintenance.controller;
import com.carmaintenance.dto.LoginRequest;
import com.carmaintenance.dto.LoginResponse;
import com.carmaintenance.dto.Result;
import com.carmaintenance.entity.User;
import com.carmaintenance.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* 认证控制器
*/
@RestController
@RequestMapping("/auth")
@CrossOrigin
public class AuthController {
@Autowired
private UserRepository userRepository;
/**
* 用户登录
*/
@PostMapping("/login")
public Result<LoginResponse> login(@RequestBody LoginRequest request) {
if (request.getUsername() == null || request.getPassword() == null) {
return Result.error("用户名和密码不能为空");
}
User user = userRepository.findByUsernameAndPassword(
request.getUsername(),
request.getPassword()
).orElse(null);
if (user == null) {
return Result.error("用户名或密码错误");
}
if (user.getStatus() != 1) {
return Result.error("账号已被禁用");
}
LoginResponse.UserInfo userInfo = new LoginResponse.UserInfo(
user.getUserId(),
user.getUsername(),
user.getRealName(),
user.getPhone(),
user.getEmail(),
user.getRole().name()
);
String token = "TOKEN_" + user.getUserId() + "_" + System.currentTimeMillis();
LoginResponse response = new LoginResponse(token, userInfo);
return Result.success("登录成功", response);
}
/**
* 用户注册
*/
@PostMapping("/register")
public Result<User> register(@RequestBody User user) {
if (userRepository.existsByUsername(user.getUsername())) {
return Result.error("用户名已存在");
}
if (userRepository.existsByPhone(user.getPhone())) {
return Result.error("手机号已被注册");
}
user.setRole(User.UserRole.customer);
user.setStatus(1);
User savedUser = userRepository.save(user);
savedUser.setPassword(null);
return Result.success("注册成功", savedUser);
}
/**
* 退出登录
*/
@PostMapping("/logout")
public Result<Void> logout() {
return Result.success("退出成功");
}
}

View File

@@ -0,0 +1,114 @@
package com.carmaintenance.controller;
import com.carmaintenance.dto.Result;
import com.carmaintenance.entity.PartsInventory;
import com.carmaintenance.repository.PartsInventoryRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 配件库存管理控制器
*/
@RestController
@RequestMapping("/parts")
@CrossOrigin
public class PartsInventoryController {
@Autowired
private PartsInventoryRepository partsInventoryRepository;
/**
* 获取所有配件
*/
@GetMapping
public Result<List<PartsInventory>> getAllParts() {
List<PartsInventory> parts = partsInventoryRepository.findAll();
return Result.success(parts);
}
/**
* 根据ID获取配件
*/
@GetMapping("/{id}")
public Result<PartsInventory> getPartById(@PathVariable Integer id) {
PartsInventory part = partsInventoryRepository.findById(id).orElse(null);
if (part == null) {
return Result.notFound("配件不存在");
}
return Result.success(part);
}
/**
* 根据类别获取配件列表
*/
@GetMapping("/category/{category}")
public Result<List<PartsInventory>> getPartsByCategory(@PathVariable String category) {
List<PartsInventory> parts = partsInventoryRepository.findByCategory(category);
return Result.success(parts);
}
/**
* 获取库存预警配件
*/
@GetMapping("/low-stock")
public Result<List<PartsInventory>> getLowStockParts() {
List<PartsInventory> allParts = partsInventoryRepository.findAll();
List<PartsInventory> lowStockParts = allParts.stream()
.filter(part -> part.getStockQuantity() <= part.getMinStock())
.collect(java.util.stream.Collectors.toList());
return Result.success(lowStockParts);
}
/**
* 创建配件
*/
@PostMapping
public Result<PartsInventory> createPart(@RequestBody PartsInventory part) {
if (partsInventoryRepository.findByPartNo(part.getPartNo()).isPresent()) {
return Result.error("配件编号已存在");
}
PartsInventory savedPart = partsInventoryRepository.save(part);
return Result.success("创建成功", savedPart);
}
/**
* 更新配件
*/
@PutMapping("/{id}")
public Result<PartsInventory> updatePart(@PathVariable Integer id, @RequestBody PartsInventory part) {
PartsInventory existingPart = partsInventoryRepository.findById(id).orElse(null);
if (existingPart == null) {
return Result.notFound("配件不存在");
}
if (part.getPartName() != null) existingPart.setPartName(part.getPartName());
if (part.getCategory() != null) existingPart.setCategory(part.getCategory());
if (part.getBrand() != null) existingPart.setBrand(part.getBrand());
if (part.getModel() != null) existingPart.setModel(part.getModel());
if (part.getUnit() != null) existingPart.setUnit(part.getUnit());
if (part.getUnitPrice() != null) existingPart.setUnitPrice(part.getUnitPrice());
if (part.getStockQuantity() != null) existingPart.setStockQuantity(part.getStockQuantity());
if (part.getMinStock() != null) existingPart.setMinStock(part.getMinStock());
if (part.getSupplier() != null) existingPart.setSupplier(part.getSupplier());
if (part.getWarehouseLocation() != null) existingPart.setWarehouseLocation(part.getWarehouseLocation());
if (part.getStatus() != null) existingPart.setStatus(part.getStatus());
if (part.getRemark() != null) existingPart.setRemark(part.getRemark());
PartsInventory updatedPart = partsInventoryRepository.save(existingPart);
return Result.success("更新成功", updatedPart);
}
/**
* 删除配件
*/
@DeleteMapping("/{id}")
public Result<Void> deletePart(@PathVariable Integer id) {
if (!partsInventoryRepository.existsById(id)) {
return Result.notFound("配件不存在");
}
partsInventoryRepository.deleteById(id);
return Result.success("删除成功");
}
}

View File

@@ -0,0 +1,140 @@
package com.carmaintenance.controller;
import com.carmaintenance.dto.Result;
import com.carmaintenance.entity.ServiceOrder;
import com.carmaintenance.repository.ServiceOrderRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* 维保工单管理控制器
*/
@RestController
@RequestMapping("/orders")
@CrossOrigin
public class ServiceOrderController {
@Autowired
private ServiceOrderRepository serviceOrderRepository;
/**
* 获取所有工单
*/
@GetMapping
public Result<List<ServiceOrder>> getAllOrders() {
List<ServiceOrder> orders = serviceOrderRepository.findAll();
return Result.success(orders);
}
/**
* 根据ID获取工单
*/
@GetMapping("/{id}")
public Result<ServiceOrder> getOrderById(@PathVariable Integer id) {
ServiceOrder order = serviceOrderRepository.findById(id).orElse(null);
if (order == null) {
return Result.notFound("工单不存在");
}
return Result.success(order);
}
/**
* 根据客户ID获取工单列表
*/
@GetMapping("/customer/{customerId}")
public Result<List<ServiceOrder>> getOrdersByCustomerId(@PathVariable Integer customerId) {
List<ServiceOrder> orders = serviceOrderRepository.findByCustomerId(customerId);
return Result.success(orders);
}
/**
* 根据车辆ID获取工单列表
*/
@GetMapping("/vehicle/{vehicleId}")
public Result<List<ServiceOrder>> getOrdersByVehicleId(@PathVariable Integer vehicleId) {
List<ServiceOrder> orders = serviceOrderRepository.findByVehicleId(vehicleId);
return Result.success(orders);
}
/**
* 根据状态获取工单列表
*/
@GetMapping("/status/{status}")
public Result<List<ServiceOrder>> getOrdersByStatus(@PathVariable String status) {
try {
ServiceOrder.OrderStatus orderStatus = ServiceOrder.OrderStatus.valueOf(status);
List<ServiceOrder> orders = serviceOrderRepository.findByStatus(orderStatus);
return Result.success(orders);
} catch (IllegalArgumentException e) {
return Result.error("状态参数错误");
}
}
/**
* 创建工单
*/
@PostMapping
public Result<ServiceOrder> createOrder(@RequestBody ServiceOrder order) {
String orderNo = generateOrderNo();
order.setOrderNo(orderNo);
order.setStatus(ServiceOrder.OrderStatus.pending);
order.setPaymentStatus(ServiceOrder.PaymentStatus.unpaid);
ServiceOrder savedOrder = serviceOrderRepository.save(order);
return Result.success("工单创建成功", savedOrder);
}
/**
* 更新工单
*/
@PutMapping("/{id}")
public Result<ServiceOrder> updateOrder(@PathVariable Integer id, @RequestBody ServiceOrder order) {
ServiceOrder existingOrder = serviceOrderRepository.findById(id).orElse(null);
if (existingOrder == null) {
return Result.notFound("工单不存在");
}
if (order.getStaffId() != null) existingOrder.setStaffId(order.getStaffId());
if (order.getArrivalTime() != null) existingOrder.setArrivalTime(order.getArrivalTime());
if (order.getStartTime() != null) existingOrder.setStartTime(order.getStartTime());
if (order.getCompleteTime() != null) existingOrder.setCompleteTime(order.getCompleteTime());
if (order.getCurrentMileage() != null) existingOrder.setCurrentMileage(order.getCurrentMileage());
if (order.getFaultDescription() != null) existingOrder.setFaultDescription(order.getFaultDescription());
if (order.getDiagnosisResult() != null) existingOrder.setDiagnosisResult(order.getDiagnosisResult());
if (order.getServiceItems() != null) existingOrder.setServiceItems(order.getServiceItems());
if (order.getPartsCost() != null) existingOrder.setPartsCost(order.getPartsCost());
if (order.getLaborCost() != null) existingOrder.setLaborCost(order.getLaborCost());
if (order.getTotalCost() != null) existingOrder.setTotalCost(order.getTotalCost());
if (order.getStatus() != null) existingOrder.setStatus(order.getStatus());
if (order.getPaymentStatus() != null) existingOrder.setPaymentStatus(order.getPaymentStatus());
if (order.getRemark() != null) existingOrder.setRemark(order.getRemark());
ServiceOrder updatedOrder = serviceOrderRepository.save(existingOrder);
return Result.success("更新成功", updatedOrder);
}
/**
* 删除工单
*/
@DeleteMapping("/{id}")
public Result<Void> deleteOrder(@PathVariable Integer id) {
if (!serviceOrderRepository.existsById(id)) {
return Result.notFound("工单不存在");
}
serviceOrderRepository.deleteById(id);
return Result.success("删除成功");
}
/**
* 生成工单编号
*/
private String generateOrderNo() {
String date = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
long count = serviceOrderRepository.count() + 1;
return "SO" + date + String.format("%04d", count);
}
}

View File

@@ -0,0 +1,128 @@
package com.carmaintenance.controller;
import com.carmaintenance.dto.Result;
import com.carmaintenance.entity.User;
import com.carmaintenance.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 用户管理控制器
*/
@RestController
@RequestMapping("/users")
@CrossOrigin
public class UserController {
@Autowired
private UserRepository userRepository;
/**
* 获取所有用户
*/
@GetMapping
public Result<List<User>> getAllUsers() {
List<User> users = userRepository.findAll();
users.forEach(user -> user.setPassword(null));
return Result.success(users);
}
/**
* 根据ID获取用户
*/
@GetMapping("/{id}")
public Result<User> getUserById(@PathVariable Integer id) {
User user = userRepository.findById(id).orElse(null);
if (user == null) {
return Result.notFound("用户不存在");
}
user.setPassword(null);
return Result.success(user);
}
/**
* 根据角色获取用户列表
*/
@GetMapping("/role/{role}")
public Result<List<User>> getUsersByRole(@PathVariable String role) {
try {
User.UserRole userRole = User.UserRole.valueOf(role);
List<User> users = userRepository.findByRole(userRole);
users.forEach(user -> user.setPassword(null));
return Result.success(users);
} catch (IllegalArgumentException e) {
return Result.error("角色参数错误");
}
}
/**
* 创建用户
*/
@PostMapping
public Result<User> createUser(@RequestBody User user) {
if (userRepository.existsByUsername(user.getUsername())) {
return Result.error("用户名已存在");
}
if (userRepository.existsByPhone(user.getPhone())) {
return Result.error("手机号已被使用");
}
User savedUser = userRepository.save(user);
savedUser.setPassword(null);
return Result.success("创建成功", savedUser);
}
/**
* 更新用户
*/
@PutMapping("/{id}")
public Result<User> updateUser(@PathVariable Integer id, @RequestBody User user) {
User existingUser = userRepository.findById(id).orElse(null);
if (existingUser == null) {
return Result.notFound("用户不存在");
}
if (user.getRealName() != null) existingUser.setRealName(user.getRealName());
if (user.getPhone() != null) existingUser.setPhone(user.getPhone());
if (user.getEmail() != null) existingUser.setEmail(user.getEmail());
if (user.getStatus() != null) existingUser.setStatus(user.getStatus());
User updatedUser = userRepository.save(existingUser);
updatedUser.setPassword(null);
return Result.success("更新成功", updatedUser);
}
/**
* 删除用户
*/
@DeleteMapping("/{id}")
public Result<Void> deleteUser(@PathVariable Integer id) {
if (!userRepository.existsById(id)) {
return Result.notFound("用户不存在");
}
userRepository.deleteById(id);
return Result.success("删除成功");
}
/**
* 修改密码
*/
@PutMapping("/{id}/password")
public Result<Void> changePassword(@PathVariable Integer id,
@RequestParam String oldPassword,
@RequestParam String newPassword) {
User user = userRepository.findById(id).orElse(null);
if (user == null) {
return Result.notFound("用户不存在");
}
if (!user.getPassword().equals(oldPassword)) {
return Result.error("原密码错误");
}
user.setPassword(newPassword);
userRepository.save(user);
return Result.success("密码修改成功");
}
}

View File

@@ -0,0 +1,112 @@
package com.carmaintenance.controller;
import com.carmaintenance.dto.Result;
import com.carmaintenance.entity.Vehicle;
import com.carmaintenance.repository.VehicleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 车辆管理控制器
*/
@RestController
@RequestMapping("/vehicles")
@CrossOrigin
public class VehicleController {
@Autowired
private VehicleRepository vehicleRepository;
/**
* 获取所有车辆
*/
@GetMapping
public Result<List<Vehicle>> getAllVehicles() {
List<Vehicle> vehicles = vehicleRepository.findAll();
return Result.success(vehicles);
}
/**
* 根据ID获取车辆
*/
@GetMapping("/{id}")
public Result<Vehicle> getVehicleById(@PathVariable Integer id) {
Vehicle vehicle = vehicleRepository.findById(id).orElse(null);
if (vehicle == null) {
return Result.notFound("车辆不存在");
}
return Result.success(vehicle);
}
/**
* 根据客户ID获取车辆列表
*/
@GetMapping("/customer/{customerId}")
public Result<List<Vehicle>> getVehiclesByCustomerId(@PathVariable Integer customerId) {
List<Vehicle> vehicles = vehicleRepository.findByCustomerId(customerId);
return Result.success(vehicles);
}
/**
* 根据车牌号查询车辆
*/
@GetMapping("/plate/{licensePlate}")
public Result<Vehicle> getVehicleByPlate(@PathVariable String licensePlate) {
Vehicle vehicle = vehicleRepository.findByLicensePlate(licensePlate).orElse(null);
if (vehicle == null) {
return Result.notFound("车辆不存在");
}
return Result.success(vehicle);
}
/**
* 创建车辆档案
*/
@PostMapping
public Result<Vehicle> createVehicle(@RequestBody Vehicle vehicle) {
if (vehicleRepository.findByLicensePlate(vehicle.getLicensePlate()).isPresent()) {
return Result.error("车牌号已存在");
}
if (vehicle.getVin() != null && vehicleRepository.findByVin(vehicle.getVin()).isPresent()) {
return Result.error("车架号已存在");
}
Vehicle savedVehicle = vehicleRepository.save(vehicle);
return Result.success("创建成功", savedVehicle);
}
/**
* 更新车辆信息
*/
@PutMapping("/{id}")
public Result<Vehicle> updateVehicle(@PathVariable Integer id, @RequestBody Vehicle vehicle) {
Vehicle existingVehicle = vehicleRepository.findById(id).orElse(null);
if (existingVehicle == null) {
return Result.notFound("车辆不存在");
}
if (vehicle.getColor() != null) existingVehicle.setColor(vehicle.getColor());
if (vehicle.getMileage() != null) existingVehicle.setMileage(vehicle.getMileage());
if (vehicle.getLastMaintenanceDate() != null)
existingVehicle.setLastMaintenanceDate(vehicle.getLastMaintenanceDate());
if (vehicle.getNextMaintenanceDate() != null)
existingVehicle.setNextMaintenanceDate(vehicle.getNextMaintenanceDate());
if (vehicle.getStatus() != null) existingVehicle.setStatus(vehicle.getStatus());
Vehicle updatedVehicle = vehicleRepository.save(existingVehicle);
return Result.success("更新成功", updatedVehicle);
}
/**
* 删除车辆
*/
@DeleteMapping("/{id}")
public Result<Void> deleteVehicle(@PathVariable Integer id) {
if (!vehicleRepository.existsById(id)) {
return Result.notFound("车辆不存在");
}
vehicleRepository.deleteById(id);
return Result.success("删除成功");
}
}

View File

@@ -0,0 +1,12 @@
package com.carmaintenance.dto;
import lombok.Data;
/**
* 登录请求DTO
*/
@Data
public class LoginRequest {
private String username;
private String password;
}

View File

@@ -0,0 +1,28 @@
package com.carmaintenance.dto;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 登录响应DTO
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginResponse {
private String token;
private UserInfo userInfo;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class UserInfo {
private Integer userId;
private String username;
private String realName;
private String phone;
private String email;
private String role;
}
}

View File

@@ -0,0 +1,54 @@
package com.carmaintenance.dto;
import lombok.Data;
/**
* 统一API响应结果
*/
@Data
public class Result<T> {
private Integer code;
private String message;
private T data;
private Result() {}
private Result(Integer code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> Result<T> success() {
return new Result<>(200, "操作成功", null);
}
public static <T> Result<T> success(T data) {
return new Result<>(200, "操作成功", data);
}
public static <T> Result<T> success(String message, T data) {
return new Result<>(200, message, data);
}
public static <T> Result<T> error(String message) {
return new Result<>(500, message, null);
}
public static <T> Result<T> error(Integer code, String message) {
return new Result<>(code, message, null);
}
public static <T> Result<T> unauthorized(String message) {
return new Result<>(401, message, null);
}
public static <T> Result<T> forbidden(String message) {
return new Result<>(403, message, null);
}
public static <T> Result<T> notFound(String message) {
return new Result<>(404, message, null);
}
}

View File

@@ -0,0 +1,66 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 预约记录实体
*/
@Data
@Entity
@Table(name = "appointments")
@EntityListeners(AuditingEntityListener.class)
public class Appointment {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "appointment_id")
private Integer appointmentId;
@Column(name = "customer_id", nullable = false)
private Integer customerId;
@Column(name = "vehicle_id", nullable = false)
private Integer vehicleId;
@Enumerated(EnumType.STRING)
@Column(name = "service_type", nullable = false)
private ServiceType serviceType;
@Column(name = "appointment_time", nullable = false)
private LocalDateTime appointmentTime;
@Column(name = "contact_phone", nullable = false, length = 20)
private String contactPhone;
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private AppointmentStatus status = AppointmentStatus.pending;
@Column(name = "order_id")
private Integer orderId;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
public enum ServiceType {
maintenance, repair, beauty, insurance
}
public enum AppointmentStatus {
pending, confirmed, completed, cancelled
}
}

View File

@@ -0,0 +1,67 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 客户信息实体
*/
@Data
@Entity
@Table(name = "customers")
@EntityListeners(AuditingEntityListener.class)
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "customer_id")
private Integer customerId;
@Column(name = "user_id", nullable = false)
private Integer userId;
@Column(name = "customer_no", unique = true, length = 50)
private String customerNo;
@Column(name = "id_card", length = 18)
private String idCard;
@Column(name = "address", length = 200)
private String address;
@Enumerated(EnumType.STRING)
@Column(name = "gender")
private Gender gender;
@Column(name = "birth_date")
private LocalDate birthDate;
@Enumerated(EnumType.STRING)
@Column(name = "membership_level")
private MembershipLevel membershipLevel = MembershipLevel.normal;
@Column(name = "points")
private Integer points = 0;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
public enum Gender {
male, female, other
}
public enum MembershipLevel {
normal, silver, gold, platinum
}
}

View File

@@ -0,0 +1,72 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 配件库存实体
*/
@Data
@Entity
@Table(name = "parts_inventory")
@EntityListeners(AuditingEntityListener.class)
public class PartsInventory {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "part_id")
private Integer partId;
@Column(name = "part_no", nullable = false, unique = true, length = 50)
private String partNo;
@Column(name = "part_name", nullable = false, length = 100)
private String partName;
@Column(name = "category", length = 50)
private String category;
@Column(name = "brand", length = 50)
private String brand;
@Column(name = "model", length = 100)
private String model;
@Column(name = "unit", length = 20)
private String unit = "";
@Column(name = "unit_price", nullable = false, precision = 10, scale = 2)
private BigDecimal unitPrice;
@Column(name = "stock_quantity")
private Integer stockQuantity = 0;
@Column(name = "min_stock")
private Integer minStock = 10;
@Column(name = "supplier", length = 100)
private String supplier;
@Column(name = "warehouse_location", length = 50)
private String warehouseLocation;
@Column(name = "status")
private Integer status = 1;
@Column(name = "remark", columnDefinition = "TEXT")
private String remark;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,54 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 服务项目实体
*/
@Data
@Entity
@Table(name = "service_items")
@EntityListeners(AuditingEntityListener.class)
public class ServiceItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "item_id")
private Integer itemId;
@Column(name = "item_code", nullable = false, unique = true, length = 50)
private String itemCode;
@Column(name = "item_name", nullable = false, length = 100)
private String itemName;
@Column(name = "category", length = 50)
private String category;
@Column(name = "standard_price", nullable = false, precision = 10, scale = 2)
private BigDecimal standardPrice;
@Column(name = "duration")
private Integer duration;
@Column(name = "description", columnDefinition = "TEXT")
private String description;
@Column(name = "status")
private Integer status = 1;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
}

View File

@@ -0,0 +1,105 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDateTime;
/**
* 维保工单实体
*/
@Data
@Entity
@Table(name = "service_orders")
@EntityListeners(AuditingEntityListener.class)
public class ServiceOrder {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "order_id")
private Integer orderId;
@Column(name = "order_no", nullable = false, unique = true, length = 50)
private String orderNo;
@Column(name = "vehicle_id", nullable = false)
private Integer vehicleId;
@Column(name = "customer_id", nullable = false)
private Integer customerId;
@Enumerated(EnumType.STRING)
@Column(name = "service_type", nullable = false)
private ServiceType serviceType;
@Column(name = "appointment_time")
private LocalDateTime appointmentTime;
@Column(name = "arrival_time")
private LocalDateTime arrivalTime;
@Column(name = "start_time")
private LocalDateTime startTime;
@Column(name = "complete_time")
private LocalDateTime completeTime;
@Column(name = "staff_id")
private Integer staffId;
@Column(name = "current_mileage", precision = 10, scale = 2)
private BigDecimal currentMileage;
@Column(name = "fault_description", columnDefinition = "TEXT")
private String faultDescription;
@Column(name = "diagnosis_result", columnDefinition = "TEXT")
private String diagnosisResult;
@Column(name = "service_items", columnDefinition = "TEXT")
private String serviceItems;
@Column(name = "parts_cost", precision = 10, scale = 2)
private BigDecimal partsCost = BigDecimal.ZERO;
@Column(name = "labor_cost", precision = 10, scale = 2)
private BigDecimal laborCost = BigDecimal.ZERO;
@Column(name = "total_cost", precision = 10, scale = 2)
private BigDecimal totalCost = BigDecimal.ZERO;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private OrderStatus status = OrderStatus.pending;
@Enumerated(EnumType.STRING)
@Column(name = "payment_status")
private PaymentStatus paymentStatus = PaymentStatus.unpaid;
@Column(name = "remark", columnDefinition = "TEXT")
private String remark;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
public enum ServiceType {
maintenance, repair, beauty, insurance
}
public enum OrderStatus {
pending, appointed, in_progress, completed, cancelled
}
public enum PaymentStatus {
unpaid, paid, refunded
}
}

View File

@@ -0,0 +1,58 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.time.LocalDateTime;
/**
* 用户实体
*/
@Data
@Entity
@Table(name = "users")
@EntityListeners(AuditingEntityListener.class)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "user_id")
private Integer userId;
@Column(name = "username", nullable = false, unique = true, length = 50)
private String username;
@Column(name = "password", nullable = false)
private String password;
@Column(name = "real_name", nullable = false, length = 50)
private String realName;
@Column(name = "phone", nullable = false, length = 20)
private String phone;
@Column(name = "email", length = 100)
private String email;
@Enumerated(EnumType.STRING)
@Column(name = "role", nullable = false)
private UserRole role;
@Column(name = "status")
private Integer status = 1;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
public enum UserRole {
admin, staff, customer
}
}

View File

@@ -0,0 +1,75 @@
package com.carmaintenance.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* 车辆档案实体
*/
@Data
@Entity
@Table(name = "vehicles")
@EntityListeners(AuditingEntityListener.class)
public class Vehicle {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "vehicle_id")
private Integer vehicleId;
@Column(name = "customer_id", nullable = false)
private Integer customerId;
@Column(name = "license_plate", nullable = false, unique = true, length = 20)
private String licensePlate;
@Column(name = "brand", nullable = false, length = 50)
private String brand;
@Column(name = "model", nullable = false, length = 50)
private String model;
@Column(name = "color", length = 20)
private String color;
@Column(name = "vin", unique = true, length = 17)
private String vin;
@Column(name = "engine_no", length = 50)
private String engineNo;
@Column(name = "purchase_date")
private LocalDate purchaseDate;
@Column(name = "mileage", precision = 10, scale = 2)
private BigDecimal mileage = BigDecimal.ZERO;
@Column(name = "last_maintenance_date")
private LocalDate lastMaintenanceDate;
@Column(name = "next_maintenance_date")
private LocalDate nextMaintenanceDate;
@Enumerated(EnumType.STRING)
@Column(name = "status")
private VehicleStatus status = VehicleStatus.normal;
@CreatedDate
@Column(name = "create_time", updatable = false)
private LocalDateTime createTime;
@LastModifiedDate
@Column(name = "update_time")
private LocalDateTime updateTime;
public enum VehicleStatus {
normal, in_service, completed
}
}

View File

@@ -0,0 +1,20 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.Appointment;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 预约记录Repository
*/
@Repository
public interface AppointmentRepository extends JpaRepository<Appointment, Integer> {
List<Appointment> findByCustomerId(Integer customerId);
List<Appointment> findByVehicleId(Integer vehicleId);
List<Appointment> findByStatus(Appointment.AppointmentStatus status);
}

View File

@@ -0,0 +1,18 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
/**
* 客户Repository
*/
@Repository
public interface CustomerRepository extends JpaRepository<Customer, Integer> {
Optional<Customer> findByUserId(Integer userId);
Optional<Customer> findByCustomerNo(String customerNo);
}

View File

@@ -0,0 +1,21 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.PartsInventory;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 配件库存Repository
*/
@Repository
public interface PartsInventoryRepository extends JpaRepository<PartsInventory, Integer> {
Optional<PartsInventory> findByPartNo(String partNo);
List<PartsInventory> findByCategory(String category);
List<PartsInventory> findByStatus(Integer status);
}

View File

@@ -0,0 +1,21 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.ServiceItem;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 服务项目Repository
*/
@Repository
public interface ServiceItemRepository extends JpaRepository<ServiceItem, Integer> {
Optional<ServiceItem> findByItemCode(String itemCode);
List<ServiceItem> findByCategory(String category);
List<ServiceItem> findByStatus(Integer status);
}

View File

@@ -0,0 +1,25 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.ServiceOrder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 维保工单Repository
*/
@Repository
public interface ServiceOrderRepository extends JpaRepository<ServiceOrder, Integer> {
Optional<ServiceOrder> findByOrderNo(String orderNo);
List<ServiceOrder> findByCustomerId(Integer customerId);
List<ServiceOrder> findByVehicleId(Integer vehicleId);
List<ServiceOrder> findByStaffId(Integer staffId);
List<ServiceOrder> findByStatus(ServiceOrder.OrderStatus status);
}

View File

@@ -0,0 +1,25 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 用户Repository
*/
@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
Optional<User> findByUsername(String username);
Optional<User> findByUsernameAndPassword(String username, String password);
List<User> findByRole(User.UserRole role);
boolean existsByUsername(String username);
boolean existsByPhone(String phone);
}

View File

@@ -0,0 +1,21 @@
package com.carmaintenance.repository;
import com.carmaintenance.entity.Vehicle;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
/**
* 车辆Repository
*/
@Repository
public interface VehicleRepository extends JpaRepository<Vehicle, Integer> {
List<Vehicle> findByCustomerId(Integer customerId);
Optional<Vehicle> findByLicensePlate(String licensePlate);
Optional<Vehicle> findByVin(String vin);
}

View File

@@ -0,0 +1,43 @@
# 应用配置
spring.application.name=car-maintenance-system
server.port=8080
server.servlet.context-path=/api
# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/car_maintenance_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
spring.datasource.username=root
spring.datasource.password=123456
# JPA配置
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect
spring.jpa.properties.hibernate.format_sql=true
# 日志配置
logging.level.root=INFO
logging.level.com.carmaintenance=DEBUG
logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss} - %msg%n
logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
# 文件上传配置
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
# Jackson配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.jackson.serialization.write-dates-as-timestamps=false
# JWT配置
jwt.secret=carMaintenanceSystemSecretKey2025
jwt.expiration=86400000
# CORS跨域配置
cors.allowed-origins=http://localhost:3000,http://localhost:5500,http://127.0.0.1:5500
cors.allowed-methods=GET,POST,PUT,DELETE,OPTIONS
cors.allowed-headers=*
cors.allow-credentials=true