编辑 | blame | 历史 | 原始文档

车辆绑定与解绑功能说明

功能概述

本系统实现了完整的用户车辆绑定与解绑功能,支持用户绑定车辆、强制重新绑定、查看当前绑定车辆以及解绑车辆。

一、功能特点

1. 绑定规则

  • ✅ 一个用户同时只能绑定一辆车
  • ✅ 一辆车可以被多个用户轮班绑定
  • ✅ 绑定时自动解绑旧车辆
  • ✅ 支持扫码绑定和下拉选择绑定
  • ✅ 强制绑定时会提示当前绑定车辆信息

2. 解绑规则

  • ✅ 支持手动解绑当前车辆
  • ✅ 解绑操作需二次确认
  • ✅ 解绑记录保留在数据库中(status=1)
  • ✅ 解绑成功后自动刷新用户信息

二、技术实现

1. 数据库设计

表名:sys_user_vehicle

CREATE TABLE IF NOT EXISTS sys_user_vehicle (
    id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
    user_id BIGINT(20) NOT NULL COMMENT '用户ID',
    vehicle_id BIGINT(20) NOT NULL COMMENT '车辆ID',
    bind_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '绑定时间',
    bind_by VARCHAR(64) DEFAULT '' COMMENT '绑定操作人',
    status CHAR(1) DEFAULT '0' COMMENT '绑定状态(0正常 1解绑)',
    remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
    create_by VARCHAR(64) DEFAULT '' COMMENT '创建者',
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    update_by VARCHAR(64) DEFAULT '' COMMENT '更新者',
    update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    PRIMARY KEY (id),
    UNIQUE KEY idx_user_vehicle (user_id, vehicle_id) COMMENT '用户车辆唯一索引',
    KEY idx_user_id (user_id) COMMENT '用户ID索引',
    KEY idx_vehicle_id (vehicle_id) COMMENT '车辆ID索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户车辆绑定表';

字段说明:
- status: 0=正常绑定,1=已解绑
- bind_time: 绑定操作时间
- update_time: 解绑操作时间

2. 后端接口

Controller层:VehicleInfoController.java

基础路径: /system/vehicle

2.1 绑定车辆
@PostMapping("/bind")
public AjaxResult bindVehicle(@RequestBody Map<String, Object> params)

请求参数:
json { "userId": 1, "vehicleId": 100 }

响应示例:
json { "code": 200, "msg": "车辆绑定成功" }

2.2 解绑车辆
@PostMapping("/unbind")
public AjaxResult unbindVehicle(@RequestBody Map<String, Object> params)

请求参数:
json { "userId": 1, "vehicleId": 100 }

响应示例:
json { "code": 200, "msg": "车辆解绑成功" }

2.3 获取用户绑定的车辆
@GetMapping("/user/bound/{userId}")
public AjaxResult getUserBoundVehicle(@PathVariable("userId") Long userId)

响应示例:
json { "code": 200, "data": { "vehicleId": 100, "vehicleNumber": "粤A12345", "vehicleType": "救护车", "vehicleBrand": "福特", "vehicleModel": "全顺" } }

Service层:VehicleInfoServiceImpl.java

2.4 绑定车辆业务逻辑
@Transactional
public int bindVehicleToUser(Long userId, Long vehicleId) {
    // 先解绑用户的所有车辆(确保一个用户只能绑定一辆车)
    vehicleInfoMapper.unbindAllVehiclesFromUser(userId);
    
    // 绑定新车辆
    return vehicleInfoMapper.bindVehicleToUser(userId, vehicleId, bindBy);
}

事务特性:
- 使用 @Transactional 保证原子性
- 先解绑再绑定,失败自动回滚

2.5 解绑车辆业务逻辑
public int unbindVehicleFromUser(Long userId, Long vehicleId) {
    return vehicleInfoMapper.unbindVehicleFromUser(userId, vehicleId);
}

Mapper层:VehicleInfoMapper.xml

2.6 SQL映射

绑定车辆:
xml <insert id="bindVehicleToUser"> INSERT INTO sys_user_vehicle (user_id, vehicle_id, bind_time, bind_by, status, create_by, create_time) VALUES (#{userId}, #{vehicleId}, NOW(), #{bindBy}, '0', #{bindBy}, NOW()) </insert>

解绑车辆:
xml <update id="unbindVehicleFromUser"> UPDATE sys_user_vehicle SET status = '1', update_time = NOW() WHERE user_id = #{userId} AND vehicle_id = #{vehicleId} AND status = '0' </update>

解绑所有车辆:
xml <update id="unbindAllVehiclesFromUser"> UPDATE sys_user_vehicle SET status = '1', update_time = NOW() WHERE user_id = #{userId} AND status = '0' </update>

获取绑定车辆:
xml <select id="getUserBoundVehicle" resultMap="VehicleInfoResult"> SELECT v.* FROM tb_vehicle_info v INNER JOIN sys_user_vehicle uv ON v.vehicle_id = uv.vehicle_id WHERE uv.user_id = #{userId} AND uv.status = '0' ORDER BY uv.bind_time DESC LIMIT 1 </select>

3. 前端实现

3.1 API封装:vehicle.js

// 绑定车辆
export function bindVehicleToUser(userId, vehicleId) {
  return request({
    url: '/system/vehicle/bind',
    method: 'post',
    data: { userId, vehicleId }
  })
}

// 解绑车辆
export function unbindVehicleFromUser(userId, vehicleId) {
  return request({
    url: '/system/vehicle/unbind',
    method: 'post',
    data: { userId, vehicleId }
  })
}

// 获取用户绑定的车辆
export function getUserBoundVehicle(userId) {
  return request({
    url: '/system/vehicle/user/bound/' + userId,
    method: 'get'
  })
}

3.2 绑定车辆页面:pages/bind-vehicle.vue

页面路径: /pages/bind-vehicle

主要功能:

  1. 加载当前绑定车辆
    javascript loadCurrentBoundVehicle() { const userId = this.currentUser.userId getUserBoundVehicle(userId).then(response => { if (response.code === 200 && response.data) { this.currentBoundVehicle = response.data } }) }

  2. 绑定车辆逻辑
    ```javascript
    bindVehicle() {
    // 检查是否选择的是当前已绑定的车辆
    if (this.currentBoundVehicle &&
    this.currentBoundVehicle.vehicleId === this.selectedVehicleId) {
    this.$modal.showToast('当前已绑定此车辆,无需重复绑定')
    return
    }

// 如果已经绑定了其他车辆,提示是否强制绑定
if (this.currentBoundVehicle) {
const confirmMsg = 您当前已绑定车辆:${currentVehicleNo}\n\n确认要解绑旧车辆并绑定新车辆:${this.selectedVehiclePlate} 吗?
this.$modal.confirm(confirmMsg).then(() => {
this.performBind()
})
} else {
// 没有绑定车辆,直接绑定
this.$modal.confirm('确认绑定车辆 ' + this.selectedVehiclePlate + ' 吗?')
.then(() => {
this.performBind()
})
}
}
```

  1. 执行绑定操作
    javascript performBind() { const userId = this.currentUser.userId bindVehicleToUser(userId, this.selectedVehicleId).then(response => { if (response.code === 200) { this.$modal.showToast('车辆绑定成功') this.$store.dispatch('GetInfo') // 刷新用户信息 setTimeout(() => { this.$tab.navigateBack() }, 1500) } }) }

3.3 个人中心页面:pages/mine/index.vue

页面路径: /pages/mine/index

主要功能:

  1. 显示绑定车辆信息
    vue <view class="info-item"> <view class="info-label">绑定车辆:</view> <view class="info-value">{{ boundVehicle || '未绑定' }}</view> </view>

  2. 解绑/绑定按钮
    ```vue


    <button class="unbind-btn" @click="unbindVehicle">取消绑定车辆


<button class="bind-btn" @click="goToBindVehicle">绑定车辆

```

  1. 获取绑定车辆信息
    ```javascript
    getUserInfo() {
    const userId = this.$store.state.user.userId

// 获取用户绑定的车辆信息
if (userId) {
getUserBoundVehicle(userId).then(response => {
if (response.code === 200 && response.data) {
const vehicle = response.data
this.boundVehicle = vehicle.vehicleNumber || '未知车牌'
this.boundVehicleId = vehicle.vehicleId
} else {
this.boundVehicle = '未绑定'
this.boundVehicleId = null
}
})
}
}
```

  1. 解绑车辆
    ```javascript
    unbindVehicle() {
    const userId = this.$store.state.user.userId
    const vehicleId = this.boundVehicleId

this.$modal.confirm(确认取消绑定车辆 ${this.boundVehicle} 吗?).then(() => {
unbindVehicleFromUser(userId, vehicleId).then(response => {
if (response.code === 200) {
this.boundVehicle = '未绑定'
this.boundVehicleId = null
this.$modal.showToast('取消绑定成功')
this.$store.dispatch('GetInfo') // 刷新用户信息
}
})
})
}
```

三、业务流程

绑定车辆流程

graph TD
    A[用户选择车辆] --> B{已绑定车辆?}
    B -->|否| C[直接绑定]
    B -->|是| D{选择相同车辆?}
    D -->|是| E[提示无需重复绑定]
    D -->|否| F[提示强制绑定确认]
    F --> G{用户确认?}
    G -->|是| H[解绑旧车辆]
    G -->|否| I[取消操作]
    H --> J[绑定新车辆]
    C --> J
    J --> K[刷新用户信息]
    K --> L[返回上一页]

解绑车辆流程

graph TD
    A[用户点击解绑] --> B{已绑定车辆?}
    B -->|否| C[提示未绑定]
    B -->|是| D[显示确认对话框]
    D --> E{用户确认?}
    E -->|是| F[调用解绑API]
    E -->|否| G[取消操作]
    F --> H{解绑成功?}
    H -->|是| I[更新绑定状态]
    H -->|否| J[显示错误信息]
    I --> K[刷新用户信息]
    K --> L[显示成功提示]

四、使用场景

场景1:首次绑定车辆

  1. 用户进入个人中心,点击"绑定车辆"按钮
  2. 跳转到绑定车辆页面
  3. 选择车牌号或扫码
  4. 确认绑定
  5. 绑定成功,返回个人中心

场景2:更换绑定车辆

  1. 用户进入绑定车辆页面
  2. 系统自动加载当前绑定车辆
  3. 选择新车辆
  4. 系统提示:您当前已绑定车辆:粤A12345,确认要解绑旧车辆并绑定新车辆:粤B67890 吗?
  5. 用户确认
  6. 系统自动解绑旧车辆并绑定新车辆
  7. 绑定成功

场景3:解绑车辆

  1. 用户进入个人中心
  2. 看到当前绑定车辆信息
  3. 点击"取消绑定车辆"按钮
  4. 系统提示:确认取消绑定车辆 粤A12345 吗?
  5. 用户确认
  6. 解绑成功,显示"未绑定"

五、注意事项

1. 数据一致性

  • 使用 @Transactional 注解确保绑定/解绑操作的原子性
  • 通过唯一索引防止重复绑定
  • 解绑操作不删除记录,只修改状态(status=1)

2. 权限控制

  • 接口使用 @Anonymous 注解,允许匿名访问
  • 实际生产环境建议添加权限验证
  • 建议增加只能解绑自己绑定的车辆的限制

3. 异常处理

  • 车辆不存在时返回错误
  • 车辆状态异常时不允许绑定
  • 网络请求失败时有友好的错误提示

4. 用户体验

  • 绑定/解绑操作都需要二次确认
  • 提示信息清晰明确
  • 操作成功后自动刷新用户信息
  • 绑定成功后延迟1.5秒自动返回

六、测试要点

功能测试

  • ✅ 首次绑定车辆
  • ✅ 强制重新绑定(替换旧车辆)
  • ✅ 重复绑定相同车辆(应提示无需重复绑定)
  • ✅ 解绑当前车辆
  • ✅ 解绑后重新绑定

异常测试

  • ✅ 绑定不存在的车辆
  • ✅ 绑定状态异常的车辆
  • ✅ 网络异常时的处理
  • ✅ 并发绑定同一辆车的处理

边界测试

  • ✅ 用户未登录时的处理
  • ✅ 车辆列表为空时的处理
  • ✅ 数据库操作失败时的回滚

七、文件清单

后端文件

  1. sql/user_vehicle_bind.sql - 数据库表结构
  2. ruoyi-system/mapper/VehicleInfoMapper.java - Mapper接口
  3. ruoyi-system/mapper/VehicleInfoMapper.xml - SQL映射
  4. ruoyi-system/service/IVehicleInfoService.java - Service接口
  5. ruoyi-system/service/impl/VehicleInfoServiceImpl.java - Service实现
  6. ruoyi-admin/controller/VehicleInfoController.java - Controller

前端文件

  1. app/api/vehicle.js - 车辆API封装
  2. app/pages/bind-vehicle.vue - 绑定车辆页面
  3. app/pages/mine/index.vue - 个人中心页面

八、版本历史

版本 日期 修改内容 修改人

| 1.0 | 2025-10-15 | 初始版本,实现基础绑定解绑功能 | - |
| 1.1 | 2025-10-15 | 修复API路径错误,完善解绑功能 | - |
| 1.2 | 2025-10-15 | 完善个人中心解绑功能实现 | - |