在 Java Web 开发领域,智慧停车场管理系统是一个典型的全栈实战项目,它涵盖了用户管理、车位预约、费用计算、数据统计等核心业务场景。使用 Spring Boot 作为后端框架,配合 Vue 3 构建现代化前端界面,能够帮助开发者掌握前后端分离架构的设计思路和实现细节。本文将基于 Spring Boot 和 Vue 3 技术栈,从零开始搭建一个功能完整的智慧停车场管理系统,重点讲解环境准备、项目结构设计、关键模块实现和常见问题排查。

1. 项目环境准备与依赖配置

在开始编码前,需要确保本地开发环境满足项目运行的基本要求。智慧停车场管理系统涉及前后端两个部分,需要分别配置相应的开发工具和运行环境。

1.1 后端开发环境要求

后端基于 Spring Boot 3.x 构建,需要 Java 17 或更高版本。建议使用 IntelliJ IDEA 作为开发 IDE,同时安装 Lombok 插件以简化实体类编写。

<!-- pom.xml 中的关键依赖配置 -->
<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Spring Boot Starter Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    
    <!-- MySQL 驱动 -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <scope>runtime</scope>
    </dependency>
    
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
    
    <!-- Spring Boot Starter Validation -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
</dependencies>

数据库使用 MySQL 8.0,需要在 application.yml 中配置数据源连接信息:

# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/parking_management?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver
    
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL8Dialect
        format_sql: true

server:
  port: 8080

1.2 前端开发环境配置

前端使用 Vue 3 组合式 API 风格,需要 Node.js 16+ 和 npm 8+ 环境。推荐使用 VS Code 作为开发工具,并安装 Volar 扩展以提供更好的 Vue 3 开发体验。

// package.json 关键依赖
{
  "dependencies": {
    "vue": "^3.3.0",
    "vue-router": "^4.2.0",
    "axios": "^1.4.0",
    "element-plus": "^2.3.0",
    "pinia": "^2.1.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.0",
    "vite": "^4.3.0",
    "typescript": "^5.0.0",
    "sass": "^1.62.0"
  }
}

前端项目使用 Vite 作为构建工具,vite.config.js 基础配置如下:

// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src')
    }
  },
  server: {
    port: 3000,
    proxy: {
      '/api': {
        target: 'http://localhost:8080',
        changeOrigin: true
      }
    }
  }
})

1.3 数据库表结构设计

停车场管理系统的核心数据模型包括用户表、停车场表、车位表、订单表等。以下是关键表的结构设计:

-- 停车场表
CREATE TABLE parking_lot (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL COMMENT '停车场名称',
    address VARCHAR(200) NOT NULL COMMENT '停车场地址',
    total_spaces INT NOT NULL COMMENT '总车位数量',
    available_spaces INT NOT NULL COMMENT '可用车位数量',
    price_per_hour DECIMAL(10,2) NOT NULL COMMENT '每小时价格',
    status TINYINT DEFAULT 1 COMMENT '状态:0-关闭,1-开放',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- 车位表
CREATE TABLE parking_space (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    parking_lot_id BIGINT NOT NULL COMMENT '所属停车场ID',
    space_number VARCHAR(20) NOT NULL COMMENT '车位编号',
    space_type TINYINT DEFAULT 1 COMMENT '车位类型:1-小型车,2-大型车',
    status TINYINT DEFAULT 0 COMMENT '状态:0-空闲,1-已预约,2-使用中',
    FOREIGN KEY (parking_lot_id) REFERENCES parking_lot(id)
);

-- 预约订单表
CREATE TABLE reservation_order (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL COMMENT '用户ID',
    parking_space_id BIGINT NOT NULL COMMENT '车位ID',
    start_time DATETIME NOT NULL COMMENT '预约开始时间',
    end_time DATETIME NOT NULL COMMENT '预约结束时间',
    total_amount DECIMAL(10,2) NOT NULL COMMENT '总金额',
    order_status TINYINT DEFAULT 0 COMMENT '订单状态:0-待支付,1-已支付,2-已完成,3-已取消',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (parking_space_id) REFERENCES parking_space(id)
);

2. 后端核心模块实现

Spring Boot 后端采用经典的三层架构:控制器层(Controller)、服务层(Service)、数据访问层(Repository)。下面重点讲解车位管理和订单处理两个核心模块的实现。

2.1 车位管理模块设计

车位管理模块负责停车场的车位信息维护、状态更新和查询功能。首先定义实体类和数据访问接口。

// ParkingSpace 实体类
@Entity
@Table(name = "parking_space")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ParkingSpace {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "parking_lot_id")
    private ParkingLot parkingLot;
    
    @Column(name = "space_number")
    private String spaceNumber;
    
    @Column(name = "space_type")
    private Integer spaceType; // 1-小型车,2-大型车
    
    @Column(name = "status")
    private Integer status; // 0-空闲,1-已预约,2-使用中
    
    @CreationTimestamp
    @Column(name = "create_time")
    private LocalDateTime createTime;
    
    @UpdateTimestamp
    @Column(name = "update_time")
    private LocalDateTime updateTime;
}

// ParkingSpaceRepository 数据访问接口
@Repository
public interface ParkingSpaceRepository extends JpaRepository<ParkingSpace, Long> {
    
    // 根据停车场ID和状态查询车位
    List<ParkingSpace> findByParkingLotIdAndStatus(Long parkingLotId, Integer status);
    
    // 根据车位编号查询
    Optional<ParkingSpace> findBySpaceNumber(String spaceNumber);
    
    // 统计某个停车场的空闲车位数量
    @Query("SELECT COUNT(ps) FROM ParkingSpace ps WHERE ps.parkingLot.id = :parkingLotId AND ps.status = 0")
    Long countAvailableSpacesByParkingLotId(@Param("parkingLotId") Long parkingLotId);
}

服务层实现车位状态管理和业务逻辑处理:

// ParkingSpaceService 接口
public interface ParkingSpaceService {
    Page<ParkingSpaceVO> getParkingSpacesByPage(ParkingSpaceQuery query);
    
    ParkingSpaceVO getParkingSpaceDetail(Long id);
    
    boolean updateParkingSpaceStatus(Long spaceId, Integer status);
    
    List<ParkingSpaceVO> getAvailableSpaces(Long parkingLotId);
}

// ParkingSpaceServiceImpl 实现类
@Service
@Transactional
@Slf4j
public class ParkingSpaceServiceImpl implements ParkingSpaceService {
    
    @Autowired
    private ParkingSpaceRepository parkingSpaceRepository;
    
    @Autowired
    private ParkingLotRepository parkingLotRepository;
    
    @Override
    public Page<ParkingSpaceVO> getParkingSpacesByPage(ParkingSpaceQuery query) {
        // 构建查询条件
        Specification<ParkingSpace> spec = (root, criteriaQuery, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            if (query.getParkingLotId() != null) {
                predicates.add(criteriaBuilder.equal(root.get("parkingLot").get("id"), query.getParkingLotId()));
            }
            
            if (query.getStatus() != null) {
                predicates.add(criteriaBuilder.equal(root.get("status"), query.getStatus()));
            }
            
            if (StringUtils.hasText(query.getSpaceNumber())) {
                predicates.add(criteriaBuilder.like(root.get("spaceNumber"), "%" + query.getSpaceNumber() + "%"));
            }
            
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        
        Pageable pageable = PageRequest.of(query.getPageNum() - 1, query.getPageSize(), 
                                          Sort.by(Sort.Direction.DESC, "createTime"));
        
        Page<ParkingSpace> parkingSpacePage = parkingSpaceRepository.findAll(spec, pageable);
        
        // 转换为VO对象
        return parkingSpacePage.map(this::convertToVO);
    }
    
    @Override
    public boolean updateParkingSpaceStatus(Long spaceId, Integer status) {
        try {
            ParkingSpace parkingSpace = parkingSpaceRepository.findById(spaceId)
                    .orElseThrow(() -> new RuntimeException("车位不存在"));
            
            parkingSpace.setStatus(status);
            parkingSpace.setUpdateTime(LocalDateTime.now());
            parkingSpaceRepository.save(parkingSpace);
            
            // 同步更新停车场的可用车位数量
            updateParkingLotAvailableSpaces(parkingSpace.getParkingLot().getId());
            
            return true;
        } catch (Exception e) {
            log.error("更新车位状态失败: spaceId={}, status={}", spaceId, status, e);
            return false;
        }
    }
    
    private void updateParkingLotAvailableSpaces(Long parkingLotId) {
        Long availableCount = parkingSpaceRepository.countAvailableSpacesByParkingLotId(parkingLotId);
        ParkingLot parkingLot = parkingLotRepository.findById(parkingLotId)
                .orElseThrow(() -> new RuntimeException("停车场不存在"));
        
        parkingLot.setAvailableSpaces(availableCount.intValue());
        parkingLotRepository.save(parkingLot);
    }
    
    private ParkingSpaceVO convertToVO(ParkingSpace parkingSpace) {
        ParkingSpaceVO vo = new ParkingSpaceVO();
        vo.setId(parkingSpace.getId());
        vo.setSpaceNumber(parkingSpace.getSpaceNumber());
        vo.setSpaceType(parkingSpace.getSpaceType());
        vo.setStatus(parkingSpace.getStatus());
        vo.setParkingLotName(parkingSpace.getParkingLot().getName());
        vo.setCreateTime(parkingSpace.getCreateTime());
        return vo;
    }
}

2.2 订单处理模块实现

订单模块处理用户的预约、支付、取消等业务流程,需要特别注意并发场景下的数据一致性。

// ReservationOrder 实体类
@Entity
@Table(name = "reservation_order")
@Data
public class ReservationOrder {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
    
    @ManyToOne
    @JoinColumn(name = "parking_space_id")
    private ParkingSpace parkingSpace;
    
    @Column(name = "start_time")
    private LocalDateTime startTime;
    
    @Column(name = "end_time")
    private LocalDateTime endTime;
    
    @Column(name = "total_amount")
    private BigDecimal totalAmount;
    
    @Column(name = "order_status")
    private Integer orderStatus; // 0-待支付,1-已支付,2-已完成,3-已取消
    
    @CreationTimestamp
    @Column(name = "create_time")
    private LocalDateTime createTime;
}

// OrderService 订单服务
@Service
@Transactional
@Slf4j
public class OrderService {
    
    @Autowired
    private ReservationOrderRepository orderRepository;
    
    @Autowired
    private ParkingSpaceService parkingSpaceService;
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    // 创建预约订单
    public ReservationOrderVO createOrder(OrderCreateDTO createDTO) {
        // 检查车位是否可用
        ParkingSpace parkingSpace = checkParkingSpaceAvailable(createDTO.getParkingSpaceId());
        
        // 计算费用
        BigDecimal amount = calculateAmount(createDTO.getStartTime(), createDTO.getEndTime(), 
                                          parkingSpace.getParkingLot().getPricePerHour());
        
        // 创建订单
        ReservationOrder order = new ReservationOrder();
        order.setParkingSpace(parkingSpace);
        order.setStartTime(createDTO.getStartTime());
        order.setEndTime(createDTO.getEndTime());
        order.setTotalAmount(amount);
        order.setOrderStatus(0); // 待支付
        
        ReservationOrder savedOrder = orderRepository.save(order);
        
        // 更新车位状态为已预约
        parkingSpaceService.updateParkingSpaceStatus(createDTO.getParkingSpaceId(), 1);
        
        return convertToVO(savedOrder);
    }
    
    // 支付订单
    public boolean payOrder(Long orderId) {
        ReservationOrder order = orderRepository.findById(orderId)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
        
        if (!order.getOrderStatus().equals(0)) {
            throw new RuntimeException("订单状态异常,无法支付");
        }
        
        // 模拟支付流程
        order.setOrderStatus(1); // 已支付
        orderRepository.save(order);
        
        // 发送支付成功消息
        sendPaymentSuccessMessage(order);
        
        return true;
    }
    
    // 取消订单
    public boolean cancelOrder(Long orderId) {
        ReservationOrder order = orderRepository.findById(orderId)
                .orElseThrow(() -> new RuntimeException("订单不存在"));
        
        if (order.getOrderStatus().equals(2) || order.getOrderStatus().equals(3)) {
            throw new RuntimeException("订单无法取消");
        }
        
        order.setOrderStatus(3); // 已取消
        orderRepository.save(order);
        
        // 释放车位
        parkingSpaceService.updateParkingSpaceStatus(order.getParkingSpace().getId(), 0);
        
        return true;
    }
    
    private ParkingSpace checkParkingSpaceAvailable(Long spaceId) {
        ParkingSpace parkingSpace = parkingSpaceRepository.findById(spaceId)
                .orElseThrow(() -> new RuntimeException("车位不存在"));
        
        if (!parkingSpace.getStatus().equals(0)) {
            throw new RuntimeException("车位当前不可用");
        }
        
        return parkingSpace;
    }
    
    private BigDecimal calculateAmount(LocalDateTime startTime, LocalDateTime endTime, BigDecimal pricePerHour) {
        long hours = Duration.between(startTime, endTime).toHours();
        if (hours < 1) {
            hours = 1; // 不足1小时按1小时计算
        }
        return pricePerHour.multiply(BigDecimal.valueOf(hours));
    }
}

3. 前端界面开发与接口对接

前端使用 Vue 3 组合式 API 和 Element Plus 组件库构建用户界面,通过 Axios 与后端 API 进行数据交互。

3.1 前端项目结构设计

src/
├── api/           # API接口封装
├── components/    # 公共组件
├── views/         # 页面组件
├── router/        # 路由配置
├── store/         # 状态管理
├── utils/         # 工具函数
└── assets/        # 静态资源

3.2 车位管理页面实现

车位管理页面展示停车场车位状态,支持按条件筛选和分页查询。

<template>
  <div class="parking-space-management">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>车位管理</span>
          <el-button type="primary" @click="handleRefresh">刷新</el-button>
        </div>
      </template>
      
      <!-- 查询条件 -->
      <el-form :model="queryParams" inline>
        <el-form-item label="停车场">
          <el-select v-model="queryParams.parkingLotId" placeholder="请选择停车场">
            <el-option 
              v-for="lot in parkingLots" 
              :key="lot.id" 
              :label="lot.name" 
              :value="lot.id"
            />
          </el-select>
        </el-form-item>
        
        <el-form-item label="车位状态">
          <el-select v-model="queryParams.status" placeholder="请选择状态">
            <el-option label="全部" value="" />
            <el-option label="空闲" :value="0" />
            <el-option label="已预约" :value="1" />
            <el-option label="使用中" :value="2" />
          </el-select>
        </el-form-item>
        
        <el-form-item>
          <el-button type="primary" @click="handleSearch">查询</el-button>
          <el-button @click="handleReset">重置</el-button>
        </el-form-item>
      </el-form>
      
      <!-- 车位列表 -->
      <el-table :data="tableData" v-loading="loading">
        <el-table-column prop="spaceNumber" label="车位编号" width="120" />
        <el-table-column prop="parkingLotName" label="所属停车场" width="200" />
        <el-table-column prop="spaceType" label="车位类型" width="100">
          <template #default="scope">
            <el-tag :type="scope.row.spaceType === 1 ? '' : 'warning'">
              {{ scope.row.spaceType === 1 ? '小型车' : '大型车' }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="status" label="状态" width="100">
          <template #default="scope">
            <el-tag 
              :type="getStatusTagType(scope.row.status)"
            >
              {{ getStatusText(scope.row.status) }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="创建时间" width="180" />
        <el-table-column label="操作" width="150">
          <template #default="scope">
            <el-button size="small" @click="handleView(scope.row)">查看</el-button>
            <el-button size="small" type="danger" @click="handleUpdateStatus(scope.row)">
              更新状态
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      
      <!-- 分页 -->
      <div class="pagination-container">
        <el-pagination
          v-model:current-page="queryParams.pageNum"
          v-model:page-size="queryParams.pageSize"
          :total="total"
          :page-sizes="[10, 20, 50, 100]"
          layout="total, sizes, prev, pager, next, jumper"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </el-card>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getParkingSpaces, updateParkingSpaceStatus } from '@/api/parkingSpace'
import { getParkingLots } from '@/api/parkingLot'

const loading = ref(false)
const tableData = ref([])
const parkingLots = ref([])
const total = ref(0)

const queryParams = reactive({
  parkingLotId: '',
  status: '',
  spaceNumber: '',
  pageNum: 1,
  pageSize: 10
})

// 获取车位列表
const fetchParkingSpaces = async () => {
  loading.value = true
  try {
    const response = await getParkingSpaces(queryParams)
    tableData.value = response.data.list
    total.value = response.data.total
  } catch (error) {
    ElMessage.error('获取车位列表失败')
  } finally {
    loading.value = false
  }
}

// 获取停车场列表
const fetchParkingLots = async () => {
  try {
    const response = await getParkingLots()
    parkingLots.value = response.data
  } catch (error) {
    ElMessage.error('获取停车场列表失败')
  }
}

// 状态标签类型
const getStatusTagType = (status) => {
  const map = {
    0: 'success', // 空闲
    1: 'warning', // 已预约
    2: 'danger'   // 使用中
  }
  return map[status] || ''
}

// 状态文本
const getStatusText = (status) => {
  const map = {
    0: '空闲',
    1: '已预约',
    2: '使用中'
  }
  return map[status] || '未知'
}

// 查询
const handleSearch = () => {
  queryParams.pageNum = 1
  fetchParkingSpaces()
}

// 重置
const handleReset = () => {
  Object.assign(queryParams, {
    parkingLotId: '',
    status: '',
    spaceNumber: '',
    pageNum: 1,
    pageSize: 10
  })
  fetchParkingSpaces()
}

// 分页大小改变
const handleSizeChange = (size) => {
  queryParams.pageSize = size
  fetchParkingSpaces()
}

// 当前页改变
const handleCurrentChange = (page) => {
  queryParams.pageNum = page
  fetchParkingSpaces()
}

// 更新车位状态
const handleUpdateStatus = async (row) => {
  try {
    await ElMessageBox.confirm('确定要更新该车位状态吗?', '提示', {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning'
    })
    
    const newStatus = row.status === 0 ? 1 : 0
    await updateParkingSpaceStatus(row.id, newStatus)
    ElMessage.success('状态更新成功')
    fetchParkingSpaces()
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('状态更新失败')
    }
  }
}

onMounted(() => {
  fetchParkingLots()
  fetchParkingSpaces()
})
</script>

3.3 API 接口封装

使用 Axios 封装 HTTP 请求,统一处理请求拦截、响应处理和错误处理。

// api/request.js
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'

// 创建axios实例
const service = axios.create({
  baseURL: '/api',
  timeout: 10000
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    // 添加token
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    
    if (res.code !== 200) {
      ElMessage.error(res.message || '请求失败')
      
      // token过期处理
      if (res.code === 401) {
        localStorage.removeItem('token')
        router.push('/login')
      }
      
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    ElMessage.error('网络错误,请检查网络连接')
    return Promise.reject(error)
  }
)

export default service

// api/parkingSpace.js
import request from './request'

// 获取车位列表
export function getParkingSpaces(params) {
  return request({
    url: '/parking-space/page',
    method: 'get',
    params
  })
}

// 更新车位状态
export function updateParkingSpaceStatus(spaceId, status) {
  return request({
    url: `/parking-space/${spaceId}/status`,
    method: 'put',
    data: { status }
  })
}

// 获取可用车位
export function getAvailableSpaces(parkingLotId) {
  return request({
    url: `/parking-space/available/${parkingLotId}`,
    method: 'get'
  })
}

4. 系统部署与性能优化

完成开发后,需要将系统部署到生产环境,并进行必要的性能优化配置。

4.1 后端部署配置

使用 Docker 容器化部署 Spring Boot 应用,确保环境一致性。

# Dockerfile
FROM openjdk:17-jdk-slim

# 设置工作目录
WORKDIR /app

# 复制jar包
COPY target/parking-management-0.0.1-SNAPSHOT.jar app.jar

# 暴露端口
EXPOSE 8080

# 启动命令
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]

生产环境配置文件 application-prod.yml:

spring:
  datasource:
    url: jdbc:mysql://mysql-server:3306/parking_management?useUnicode=true&characterEncoding=utf8
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
  
  jpa:
    hibernate:
      ddl-auto: validate
    show-sql: false
  
  redis:
    host: redis-server
    port: 6379
    password: ${REDIS_PASSWORD}
    timeout: 3000
    lettuce:
      pool:
        max-active: 20
        max-wait: -1
        max-idle: 10
        min-idle: 0

server:
  port: 8080
  tomcat:
    max-connections: 10000
    max-threads: 200
    min-spare-threads: 10

4.2 前端构建与部署

使用 Vite 构建生产版本,配置 Nginx 作为静态文件服务器。

// vite.config.prod.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  build: {
    outDir: 'dist',
    assetsDir: 'assets',
    rollupOptions: {
      output: {
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
      }
    }
  },
  server: {
    port: 3000
  }
})

Nginx 配置示例:

server {
    listen 80;
    server_name your-domain.com;
    
    # 前端静态文件
    location / {
        root /usr/share/nginx/html;
        index index.html;
        try_files $uri $uri/ /index.html;
    }
    
    # 后端API代理
    location /api/ {
        proxy_pass http://backend-server:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    
    # 静态资源缓存
    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 1y;
        add_header Cache-Control "public, immutable";
    }
}

4.3 数据库性能优化

针对停车场管理系统的高并发场景,需要对数据库进行性能优化。

-- 为常用查询字段添加索引
CREATE INDEX idx_parking_space_status ON parking_space(status);
CREATE INDEX idx_parking_space_lot_id ON parking_space(parking_lot_id);
CREATE INDEX idx_order_user_id ON reservation_order(user_id);
CREATE INDEX idx_order_status ON reservation_order(order_status);
CREATE INDEX idx_order_create_time ON reservation_order(create_time);

-- 分区表处理历史数据(按月份分区)
ALTER TABLE reservation_order PARTITION BY RANGE (YEAR(create_time)*100 + MONTH(create_time)) (
    PARTITION p202401 VALUES LESS THAN (202402),
    PARTITION p202402 VALUES LESS THAN (202403),
    PARTITION p202403 VALUES LESS THAN (202404),
    PARTITION p_max VALUES LESS THAN MAXVALUE
);

4.4 缓存策略设计

使用 Redis 缓存热点数据,减少数据库访问压力。

// Redis配置类
@Configuration
public class RedisConfig {
    
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        
        // 使用Jackson2JsonRedisSerializer序列化
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(Lazy
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐