From f006ed4c89482a17f5cfa3c110934863fe9e5066 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=8B=E5=AD=90=E7=90=A6?=
<95332614+wangziqi0409@users.noreply.github.com>
Date: Wed, 14 Jan 2026 15:11:25 +0800
Subject: [PATCH] add
---
.gitignore | 16 +
README.md | 39 +
backend/pom.xml | 76 +
backend/sql/init.sql | 85 +
.../java/com/car/CarRentalApplication.java | 11 +
.../java/com/car/common/ApiException.java | 13 +
.../main/java/com/car/common/ApiResponse.java | 26 +
.../main/java/com/car/common/ErrorCode.java | 10 +
.../car/common/GlobalExceptionHandler.java | 39 +
.../java/com/car/config/SecurityConfig.java | 13 +
.../java/com/car/config/StpInterfaceImpl.java | 32 +
.../main/java/com/car/config/WebConfig.java | 17 +
.../com/car/controller/AdminController.java | 107 ++
.../com/car/controller/AuthController.java | 44 +
.../com/car/controller/CarController.java | 29 +
.../com/car/controller/UserController.java | 109 ++
.../main/java/com/car/dto/BalanceRequest.java | 10 +
.../main/java/com/car/dto/LoginRequest.java | 12 +
.../java/com/car/dto/OrderCreateRequest.java | 16 +
.../src/main/java/com/car/dto/PayRequest.java | 13 +
.../java/com/car/dto/RealNameRequest.java | 14 +
.../java/com/car/dto/RegisterRequest.java | 14 +
backend/src/main/java/com/car/entity/Car.java | 25 +
.../main/java/com/car/entity/Favorite.java | 13 +
.../src/main/java/com/car/entity/Order.java | 25 +
.../src/main/java/com/car/entity/Payment.java | 15 +
.../src/main/java/com/car/entity/User.java | 24 +
.../main/java/com/car/mapper/CarMapper.java | 20 +
.../java/com/car/mapper/FavoriteMapper.java | 18 +
.../main/java/com/car/mapper/OrderMapper.java | 27 +
.../java/com/car/mapper/PaymentMapper.java | 14 +
.../main/java/com/car/mapper/StatsMapper.java | 13 +
.../main/java/com/car/mapper/UserMapper.java | 20 +
.../main/java/com/car/service/CarService.java | 54 +
.../java/com/car/service/FavoriteService.java | 41 +
.../java/com/car/service/OrderService.java | 151 ++
.../java/com/car/service/StatsService.java | 23 +
.../java/com/car/service/UserService.java | 126 ++
.../src/main/java/com/car/util/AuthUtil.java | 9 +
backend/src/main/resources/application.yml | 26 +
.../src/main/resources/mappers/CarMapper.xml | 71 +
.../main/resources/mappers/FavoriteMapper.xml | 29 +
.../main/resources/mappers/OrderMapper.xml | 81 +
.../main/resources/mappers/PaymentMapper.xml | 23 +
.../main/resources/mappers/StatsMapper.xml | 21 +
.../src/main/resources/mappers/UserMapper.xml | 65 +
frontend/.gitignore | 24 +
frontend/README.md | 5 +
frontend/index.html | 13 +
frontend/package-lock.json | 1684 +++++++++++++++++
frontend/package.json | 23 +
frontend/public/vite.svg | 1 +
frontend/src/App.vue | 82 +
frontend/src/api/http.js | 28 +
frontend/src/assets/vue.svg | 1 +
frontend/src/components/HelloWorld.vue | 43 +
frontend/src/main.js | 13 +
frontend/src/router/index.js | 46 +
frontend/src/store/auth.js | 27 +
frontend/src/style.css | 60 +
frontend/src/views/CarDetail.vue | 97 +
frontend/src/views/Favorites.vue | 45 +
frontend/src/views/Home.vue | 58 +
frontend/src/views/Login.vue | 43 +
frontend/src/views/Orders.vue | 76 +
frontend/src/views/Profile.vue | 91 +
frontend/src/views/Register.vue | 46 +
frontend/src/views/admin/AdminCars.vue | 133 ++
frontend/src/views/admin/AdminOrders.vue | 50 +
frontend/src/views/admin/AdminStats.vue | 40 +
frontend/src/views/admin/AdminUsers.vue | 76 +
frontend/vite.config.js | 7 +
张天明-车智车辆租赁平台的设计与实现.md | 141 ++
73 files changed, 4632 insertions(+)
create mode 100644 .gitignore
create mode 100644 backend/pom.xml
create mode 100644 backend/sql/init.sql
create mode 100644 backend/src/main/java/com/car/CarRentalApplication.java
create mode 100644 backend/src/main/java/com/car/common/ApiException.java
create mode 100644 backend/src/main/java/com/car/common/ApiResponse.java
create mode 100644 backend/src/main/java/com/car/common/ErrorCode.java
create mode 100644 backend/src/main/java/com/car/common/GlobalExceptionHandler.java
create mode 100644 backend/src/main/java/com/car/config/SecurityConfig.java
create mode 100644 backend/src/main/java/com/car/config/StpInterfaceImpl.java
create mode 100644 backend/src/main/java/com/car/config/WebConfig.java
create mode 100644 backend/src/main/java/com/car/controller/AdminController.java
create mode 100644 backend/src/main/java/com/car/controller/AuthController.java
create mode 100644 backend/src/main/java/com/car/controller/CarController.java
create mode 100644 backend/src/main/java/com/car/controller/UserController.java
create mode 100644 backend/src/main/java/com/car/dto/BalanceRequest.java
create mode 100644 backend/src/main/java/com/car/dto/LoginRequest.java
create mode 100644 backend/src/main/java/com/car/dto/OrderCreateRequest.java
create mode 100644 backend/src/main/java/com/car/dto/PayRequest.java
create mode 100644 backend/src/main/java/com/car/dto/RealNameRequest.java
create mode 100644 backend/src/main/java/com/car/dto/RegisterRequest.java
create mode 100644 backend/src/main/java/com/car/entity/Car.java
create mode 100644 backend/src/main/java/com/car/entity/Favorite.java
create mode 100644 backend/src/main/java/com/car/entity/Order.java
create mode 100644 backend/src/main/java/com/car/entity/Payment.java
create mode 100644 backend/src/main/java/com/car/entity/User.java
create mode 100644 backend/src/main/java/com/car/mapper/CarMapper.java
create mode 100644 backend/src/main/java/com/car/mapper/FavoriteMapper.java
create mode 100644 backend/src/main/java/com/car/mapper/OrderMapper.java
create mode 100644 backend/src/main/java/com/car/mapper/PaymentMapper.java
create mode 100644 backend/src/main/java/com/car/mapper/StatsMapper.java
create mode 100644 backend/src/main/java/com/car/mapper/UserMapper.java
create mode 100644 backend/src/main/java/com/car/service/CarService.java
create mode 100644 backend/src/main/java/com/car/service/FavoriteService.java
create mode 100644 backend/src/main/java/com/car/service/OrderService.java
create mode 100644 backend/src/main/java/com/car/service/StatsService.java
create mode 100644 backend/src/main/java/com/car/service/UserService.java
create mode 100644 backend/src/main/java/com/car/util/AuthUtil.java
create mode 100644 backend/src/main/resources/application.yml
create mode 100644 backend/src/main/resources/mappers/CarMapper.xml
create mode 100644 backend/src/main/resources/mappers/FavoriteMapper.xml
create mode 100644 backend/src/main/resources/mappers/OrderMapper.xml
create mode 100644 backend/src/main/resources/mappers/PaymentMapper.xml
create mode 100644 backend/src/main/resources/mappers/StatsMapper.xml
create mode 100644 backend/src/main/resources/mappers/UserMapper.xml
create mode 100644 frontend/.gitignore
create mode 100644 frontend/README.md
create mode 100644 frontend/index.html
create mode 100644 frontend/package-lock.json
create mode 100644 frontend/package.json
create mode 100644 frontend/public/vite.svg
create mode 100644 frontend/src/App.vue
create mode 100644 frontend/src/api/http.js
create mode 100644 frontend/src/assets/vue.svg
create mode 100644 frontend/src/components/HelloWorld.vue
create mode 100644 frontend/src/main.js
create mode 100644 frontend/src/router/index.js
create mode 100644 frontend/src/store/auth.js
create mode 100644 frontend/src/style.css
create mode 100644 frontend/src/views/CarDetail.vue
create mode 100644 frontend/src/views/Favorites.vue
create mode 100644 frontend/src/views/Home.vue
create mode 100644 frontend/src/views/Login.vue
create mode 100644 frontend/src/views/Orders.vue
create mode 100644 frontend/src/views/Profile.vue
create mode 100644 frontend/src/views/Register.vue
create mode 100644 frontend/src/views/admin/AdminCars.vue
create mode 100644 frontend/src/views/admin/AdminOrders.vue
create mode 100644 frontend/src/views/admin/AdminStats.vue
create mode 100644 frontend/src/views/admin/AdminUsers.vue
create mode 100644 frontend/vite.config.js
create mode 100644 张天明-车智车辆租赁平台的设计与实现.md
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..490f154
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,16 @@
+# Maven
+/backend/target/
+
+# Node
+/frontend/node_modules/
+/frontend/dist/
+
+# Logs
+*.log
+
+# OS / IDE
+.DS_Store
+Thumbs.db
+.idea/
+.vscode/
+*.iml
diff --git a/README.md b/README.md
index e69de29..1c41bc0 100644
--- a/README.md
+++ b/README.md
@@ -0,0 +1,39 @@
+车智车辆租赁平台(Vue + Spring Boot + MyBatis + MySQL + Arco Design Vue)
+
+## 功能模块
+
+用户端:
+- 登录注册、可租车辆、车辆详情、收藏夹、租车下单、归还车辆
+- 实名认证、余额支付(模拟)、个人中心、我的订单
+
+管理员端:
+- 管理员登录、车辆管理、特价车辆
+- 订单管理、实名审核、用户管理
+- 数据统计、财务报表
+
+## 后端启动
+
+1. 初始化数据库(MySQL)
+ - 执行 `backend/sql/init.sql`
+ - 默认管理员账号:`admin`,密码:`admin123`
+2. 修改配置
+ - `backend/src/main/resources/application.yml` 中的数据库连接信息
+3. 启动
+ - 在 `backend` 目录执行:`mvn spring-boot:run`
+
+## 前端启动
+
+1. 安装依赖
+ - `cd frontend`
+ - `npm install`
+2. 启动
+ - `npm run dev`
+3. 访问
+ - 前端地址:`http://localhost:5173`
+ - 后端地址:`http://localhost:8080`
+
+## 说明
+
+余额支付、实名认证为模拟流程:
+- 余额可在个人中心“余额充值”完成
+- 实名认证提交后由管理员审核
diff --git a/backend/pom.xml b/backend/pom.xml
new file mode 100644
index 0000000..6454b5e
--- /dev/null
+++ b/backend/pom.xml
@@ -0,0 +1,76 @@
+
+ 4.0.0
+ com.car
+ car-rental
+ 1.0.0
+ car-rental
+ Car rental platform
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 3.2.5
+
+
+
+
+ 17
+ 3.0.3
+ 1.38.0
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-jdbc
+
+
+ org.mybatis.spring.boot
+ mybatis-spring-boot-starter
+ ${mybatis.version}
+
+
+ com.mysql
+ mysql-connector-j
+ runtime
+
+
+ cn.dev33
+ sa-token-spring-boot3-starter
+ ${sa.token.version}
+
+
+ cn.dev33
+ sa-token-jwt
+ ${sa.token.version}
+
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
+
+ org.springframework.security
+ spring-security-crypto
+
+
+ org.projectlombok
+ lombok
+ true
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/backend/sql/init.sql b/backend/sql/init.sql
new file mode 100644
index 0000000..e7bd922
--- /dev/null
+++ b/backend/sql/init.sql
@@ -0,0 +1,85 @@
+create database if not exists car_rental default character set utf8mb4 collate utf8mb4_general_ci;
+use car_rental;
+
+create table if not exists users (
+ id bigint primary key auto_increment,
+ username varchar(50) not null unique,
+ password varchar(255) not null,
+ phone varchar(50),
+ email varchar(100),
+ role varchar(20) not null,
+ status varchar(20) not null,
+ balance decimal(10,2) default 0,
+ real_name_status varchar(20) default 'NONE',
+ real_name varchar(50),
+ id_number varchar(30),
+ id_front varchar(255),
+ id_back varchar(255),
+ created_at datetime,
+ updated_at datetime
+);
+
+create table if not exists cars (
+ id bigint primary key auto_increment,
+ brand varchar(50),
+ model varchar(50),
+ plate_no varchar(50),
+ price_per_day decimal(10,2),
+ deposit decimal(10,2),
+ status varchar(20),
+ is_special tinyint(1) default 0,
+ images text,
+ description text,
+ seats int,
+ transmission varchar(20),
+ fuel_type varchar(20),
+ mileage int,
+ created_at datetime,
+ updated_at datetime
+);
+
+create table if not exists favorites (
+ id bigint primary key auto_increment,
+ user_id bigint not null,
+ car_id bigint not null,
+ created_at datetime,
+ unique key uk_user_car(user_id, car_id)
+);
+
+create table if not exists orders (
+ id bigint primary key auto_increment,
+ order_no varchar(64) not null unique,
+ user_id bigint not null,
+ car_id bigint not null,
+ start_date date,
+ end_date date,
+ days int,
+ price_per_day decimal(10,2),
+ deposit decimal(10,2),
+ total_amount decimal(10,2),
+ status varchar(20),
+ pay_type varchar(20),
+ paid_at datetime,
+ created_at datetime,
+ updated_at datetime
+);
+
+create table if not exists payments (
+ id bigint primary key auto_increment,
+ order_id bigint not null,
+ user_id bigint not null,
+ amount decimal(10,2),
+ type varchar(20),
+ created_at datetime
+);
+
+insert into users(username, password, phone, email, role, status, balance, real_name_status, created_at, updated_at)
+values ('admin', '$2a$10$7hErhcmS8xj6QcGbe0yE0eDBn5OQWw4tGxqVylxYxe3CxbJc88x76', '13800000000', 'admin@example.com', 'ADMIN', 'ACTIVE', 0, 'NONE', now(), now())
+on duplicate key update username = username;
+
+insert into cars(brand, model, plate_no, price_per_day, deposit, status, is_special, seats, transmission, fuel_type, mileage, description, created_at, updated_at)
+values
+('丰田', '卡罗拉', '粤A12345', 200, 1000, 'AVAILABLE', 0, 5, 'AT', '汽油', 32000, '经济实用,适合通勤', now(), now()),
+('特斯拉', 'Model 3', '粤B54321', 500, 2000, 'AVAILABLE', 1, 5, 'AT', '电动', 18000, '电动轿跑,舒适安静', now(), now()),
+('本田', 'CR-V', '粤C67890', 350, 1500, 'AVAILABLE', 0, 5, 'AT', '汽油', 40000, '空间大,适合家庭出行', now(), now())
+on duplicate key update plate_no = plate_no;
diff --git a/backend/src/main/java/com/car/CarRentalApplication.java b/backend/src/main/java/com/car/CarRentalApplication.java
new file mode 100644
index 0000000..7a7db62
--- /dev/null
+++ b/backend/src/main/java/com/car/CarRentalApplication.java
@@ -0,0 +1,11 @@
+package com.car;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class CarRentalApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(CarRentalApplication.class, args);
+ }
+}
diff --git a/backend/src/main/java/com/car/common/ApiException.java b/backend/src/main/java/com/car/common/ApiException.java
new file mode 100644
index 0000000..dc57ede
--- /dev/null
+++ b/backend/src/main/java/com/car/common/ApiException.java
@@ -0,0 +1,13 @@
+package com.car.common;
+
+import lombok.Getter;
+
+@Getter
+public class ApiException extends RuntimeException {
+ private final int code;
+
+ public ApiException(int code, String message) {
+ super(message);
+ this.code = code;
+ }
+}
diff --git a/backend/src/main/java/com/car/common/ApiResponse.java b/backend/src/main/java/com/car/common/ApiResponse.java
new file mode 100644
index 0000000..9bd1e87
--- /dev/null
+++ b/backend/src/main/java/com/car/common/ApiResponse.java
@@ -0,0 +1,26 @@
+package com.car.common;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class ApiResponse {
+ private int code;
+ private String message;
+ private T data;
+
+ public static ApiResponse ok(T data) {
+ return new ApiResponse<>(0, "OK", data);
+ }
+
+ public static ApiResponse ok() {
+ return new ApiResponse<>(0, "OK", null);
+ }
+
+ public static ApiResponse error(int code, String message) {
+ return new ApiResponse<>(code, message, null);
+ }
+}
diff --git a/backend/src/main/java/com/car/common/ErrorCode.java b/backend/src/main/java/com/car/common/ErrorCode.java
new file mode 100644
index 0000000..13be7ee
--- /dev/null
+++ b/backend/src/main/java/com/car/common/ErrorCode.java
@@ -0,0 +1,10 @@
+package com.car.common;
+
+public class ErrorCode {
+ public static final int BAD_REQUEST = 400;
+ public static final int UNAUTHORIZED = 401;
+ public static final int FORBIDDEN = 403;
+ public static final int NOT_FOUND = 404;
+ public static final int CONFLICT = 409;
+ public static final int SERVER_ERROR = 500;
+}
diff --git a/backend/src/main/java/com/car/common/GlobalExceptionHandler.java b/backend/src/main/java/com/car/common/GlobalExceptionHandler.java
new file mode 100644
index 0000000..4581066
--- /dev/null
+++ b/backend/src/main/java/com/car/common/GlobalExceptionHandler.java
@@ -0,0 +1,39 @@
+package com.car.common;
+
+import cn.dev33.satoken.exception.NotLoginException;
+import jakarta.validation.ConstraintViolationException;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+
+@RestControllerAdvice
+public class GlobalExceptionHandler {
+
+ @ExceptionHandler(ApiException.class)
+ public ApiResponse handleApiException(ApiException ex) {
+ return ApiResponse.error(ex.getCode(), ex.getMessage());
+ }
+
+ @ExceptionHandler(NotLoginException.class)
+ public ApiResponse handleNotLogin(NotLoginException ex) {
+ return ApiResponse.error(ErrorCode.UNAUTHORIZED, "请先登录");
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public ApiResponse handleValid(MethodArgumentNotValidException ex) {
+ String msg = ex.getBindingResult().getAllErrors().isEmpty()
+ ? "参数错误"
+ : ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
+ return ApiResponse.error(ErrorCode.BAD_REQUEST, msg);
+ }
+
+ @ExceptionHandler(ConstraintViolationException.class)
+ public ApiResponse handleConstraint(ConstraintViolationException ex) {
+ return ApiResponse.error(ErrorCode.BAD_REQUEST, ex.getMessage());
+ }
+
+ @ExceptionHandler(Exception.class)
+ public ApiResponse handleGeneric(Exception ex) {
+ return ApiResponse.error(ErrorCode.SERVER_ERROR, ex.getMessage());
+ }
+}
diff --git a/backend/src/main/java/com/car/config/SecurityConfig.java b/backend/src/main/java/com/car/config/SecurityConfig.java
new file mode 100644
index 0000000..8a81f18
--- /dev/null
+++ b/backend/src/main/java/com/car/config/SecurityConfig.java
@@ -0,0 +1,13 @@
+package com.car.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+
+@Configuration
+public class SecurityConfig {
+ @Bean
+ public BCryptPasswordEncoder passwordEncoder() {
+ return new BCryptPasswordEncoder();
+ }
+}
diff --git a/backend/src/main/java/com/car/config/StpInterfaceImpl.java b/backend/src/main/java/com/car/config/StpInterfaceImpl.java
new file mode 100644
index 0000000..be47699
--- /dev/null
+++ b/backend/src/main/java/com/car/config/StpInterfaceImpl.java
@@ -0,0 +1,32 @@
+package com.car.config;
+
+import cn.dev33.satoken.stp.StpInterface;
+import com.car.entity.User;
+import com.car.mapper.UserMapper;
+import org.springframework.stereotype.Component;
+
+import java.util.Collections;
+import java.util.List;
+
+@Component
+public class StpInterfaceImpl implements StpInterface {
+ private final UserMapper userMapper;
+
+ public StpInterfaceImpl(UserMapper userMapper) {
+ this.userMapper = userMapper;
+ }
+
+ @Override
+ public List getPermissionList(Object loginId, String loginType) {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public List getRoleList(Object loginId, String loginType) {
+ User user = userMapper.findById(Long.valueOf(loginId.toString()));
+ if (user == null || user.getRole() == null) {
+ return Collections.emptyList();
+ }
+ return Collections.singletonList(user.getRole());
+ }
+}
diff --git a/backend/src/main/java/com/car/config/WebConfig.java b/backend/src/main/java/com/car/config/WebConfig.java
new file mode 100644
index 0000000..80f501c
--- /dev/null
+++ b/backend/src/main/java/com/car/config/WebConfig.java
@@ -0,0 +1,17 @@
+package com.car.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+public class WebConfig implements WebMvcConfigurer {
+ @Override
+ public void addCorsMappings(CorsRegistry registry) {
+ registry.addMapping("/**")
+ .allowedOriginPatterns("*")
+ .allowedMethods("*")
+ .allowedHeaders("*")
+ .allowCredentials(true);
+ }
+}
diff --git a/backend/src/main/java/com/car/controller/AdminController.java b/backend/src/main/java/com/car/controller/AdminController.java
new file mode 100644
index 0000000..12f8a49
--- /dev/null
+++ b/backend/src/main/java/com/car/controller/AdminController.java
@@ -0,0 +1,107 @@
+package com.car.controller;
+
+import cn.dev33.satoken.stp.StpUtil;
+import com.car.common.ApiResponse;
+import com.car.common.ApiException;
+import com.car.common.ErrorCode;
+import com.car.entity.Car;
+import com.car.entity.Order;
+import com.car.entity.User;
+import com.car.service.CarService;
+import com.car.service.OrderService;
+import com.car.service.StatsService;
+import com.car.service.UserService;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/api/admin")
+public class AdminController {
+ private final UserService userService;
+ private final CarService carService;
+ private final OrderService orderService;
+ private final StatsService statsService;
+
+ public AdminController(UserService userService, CarService carService, OrderService orderService, StatsService statsService) {
+ this.userService = userService;
+ this.carService = carService;
+ this.orderService = orderService;
+ this.statsService = statsService;
+ }
+
+ private void checkAdmin() {
+ if (!StpUtil.hasRole("ADMIN")) {
+ throw new ApiException(ErrorCode.FORBIDDEN, "需要管理员权限");
+ }
+ }
+
+ @GetMapping("/users")
+ public ApiResponse> listUsers(@RequestParam(required = false) String keyword) {
+ checkAdmin();
+ return ApiResponse.ok(userService.listUsers(keyword));
+ }
+
+ @PostMapping("/users/{userId}/status")
+ public ApiResponse changeStatus(@PathVariable Long userId, @RequestParam String status) {
+ checkAdmin();
+ return ApiResponse.ok(userService.changeStatus(userId, status));
+ }
+
+ @PostMapping("/real-name/{userId}/approve")
+ public ApiResponse approve(@PathVariable Long userId) {
+ checkAdmin();
+ return ApiResponse.ok(userService.reviewRealName(userId, true));
+ }
+
+ @PostMapping("/real-name/{userId}/reject")
+ public ApiResponse reject(@PathVariable Long userId) {
+ checkAdmin();
+ return ApiResponse.ok(userService.reviewRealName(userId, false));
+ }
+
+ @GetMapping("/cars")
+ public ApiResponse> listCars(@RequestParam(required = false) String keyword,
+ @RequestParam(required = false) Boolean isSpecial) {
+ checkAdmin();
+ return ApiResponse.ok(carService.list(keyword, isSpecial));
+ }
+
+ @PostMapping("/cars")
+ public ApiResponse createCar(@RequestBody Car car) {
+ checkAdmin();
+ return ApiResponse.ok(carService.create(car));
+ }
+
+ @PutMapping("/cars")
+ public ApiResponse updateCar(@RequestBody Car car) {
+ checkAdmin();
+ return ApiResponse.ok(carService.update(car));
+ }
+
+ @DeleteMapping("/cars/{id}")
+ public ApiResponse deleteCar(@PathVariable Long id) {
+ checkAdmin();
+ carService.delete(id);
+ return ApiResponse.ok();
+ }
+
+ @GetMapping("/orders")
+ public ApiResponse> listOrders(@RequestParam(required = false) String status) {
+ checkAdmin();
+ return ApiResponse.ok(orderService.listAll(status));
+ }
+
+ @GetMapping("/stats/orders")
+ public ApiResponse