# 车辆删除功能最终修复 - 使用 MyBatis-Plus 的 @TableLogic

## 问题描述
用户反馈：**删除车辆返回成功，但前端页面依然显示该记录**。

后端日志显示：
```
UPDATE vehicle SET license_plate=?, ..., update_time=?, updated_by=? 
WHERE id=? AND is_delete=0
Updates: 1
```

**关键发现**：UPDATE 语句中**没有 `is_delete` 字段**！

## 问题根源

### @TableLogic 注解的副作用

`Vehicle` 实体类使用了 `@TableLogic` 注解：
```java
@TableLogic(value = "0", delval = "1")
@TableField("is_delete")
private Integer isDelete;
```

**@TableLogic 的行为**：
1. ✅ **SELECT**: 自动添加 `WHERE is_delete = 0`
2. ✅ **DELETE**: 自动转换为 `UPDATE SET is_delete = 1`
3. ❌ **UPDATE**: `is_delete` 字段**被排除在 SET 子句之外**！

### 错误的实现方式

```java
// ❌ 错误：使用 updateById 无法修改 is_delete 字段
Vehicle vehicle = vehicleMapper.selectById(id);
vehicle.setIsDelete(1);  // 这个设置会被忽略！
vehicleMapper.updateById(vehicle);

// 生成的 SQL（注意没有 is_delete）:
UPDATE vehicle 
SET license_plate=?, car_front_photo=?, ..., update_time=?, updated_by=? 
WHERE id=? AND is_delete=0
```

**结果**：
- `is_delete` 字段没有被更新，仍然是 `0`
- 记录依然存在，前端列表继续显示
- 虽然返回 `Updates: 1`（更新了其他字段），但逻辑删除失败

## 最终解决方案

### 使用 MyBatis-Plus 的 deleteById() 方法

**正确做法**：利用 `@TableLogic` 注解，直接调用 `deleteById()`

```java
/**
 * 删除车辆（逻辑删除，使用 MyBatis-Plus 的 deleteById 自动处理）
 * 因为实体类使用了 @TableLogic 注解，deleteById 会自动将 is_delete 设置为 1
 */
@Override
@Transactional
public AjaxResult deleteVehicle(Integer id) {
    System.out.println("[VEHICLE-DELETE] 开始逻辑删除车辆，ID: " + id);
    
    if (id == null) {
        System.out.println("[VEHICLE-DELETE] 删除失败：车辆ID为空");
        return AjaxResult.error("车辆ID不能为空");
    }

    // 查询车辆是否存在（@TableLogic 会自动过滤 is_delete=1 的记录）
    Vehicle vehicle = vehicleMapper.selectById(id);
    if (vehicle == null) {
        System.out.println("[VEHICLE-DELETE] 删除失败：车辆不存在或已被删除，ID: " + id);
        return AjaxResult.error("车辆不存在");
    }

    System.out.println("[VEHICLE-DELETE] 车辆信息 - 车牌号: " + vehicle.getLicensePlate() + ", 当前 is_delete: " + vehicle.getIsDelete());

    // 获取当前用户ID（用于日志记录）
    Integer userId = SecurityUtil.getCurrentUserId();
    
    // ✅ 使用 MyBatis-Plus 的 deleteById 方法
    // 因为实体类有 @TableLogic 注解，这会自动执行逻辑删除（UPDATE SET is_delete=1）
    // 而不是物理删除（DELETE FROM）
    boolean success = vehicleMapper.deleteById(id) > 0;

    if (success) {
        System.out.println("[VEHICLE-DELETE] ✅ 逻辑删除成功，车牌号: " + vehicle.getLicensePlate() + ", 操作人ID: " + userId);
        return AjaxResult.success("删除车辆成功");
    } else {
        System.out.println("[VEHICLE-DELETE] ❌ 逻辑删除失败，车牌号: " + vehicle.getLicensePlate());
        return AjaxResult.error("删除车辆失败");
    }
}
```

### 生成的 SQL

**使用 deleteById() 后**：
```sql
UPDATE vehicle 
SET is_delete = 1 
WHERE id = 3;
```

**完美！** 只更新 `is_delete` 字段，不涉及其他字段。

## MyBatis-Plus @TableLogic 工作原理

### 1. SELECT 操作
```java
Vehicle vehicle = vehicleMapper.selectById(1);
```
**生成的 SQL**：
```sql
SELECT * FROM vehicle WHERE id = 1 AND is_delete = 0
```

### 2. DELETE 操作（逻辑删除）
```java
vehicleMapper.deleteById(1);
```
**生成的 SQL**：
```sql
UPDATE vehicle SET is_delete = 1 WHERE id = 1
```

### 3. UPDATE 操作（注意！）
```java
vehicle.setIsDelete(1);
vehicleMapper.updateById(vehicle);
```
**生成的 SQL**：
```sql
-- ❌ is_delete 不在 SET 子句中！
UPDATE vehicle 
SET license_plate=?, car_front_photo=?, ..., update_time=? 
WHERE id=? AND is_delete=0
```

**结论**：
- ✅ **删除时用 `deleteById()`**，它会自动设置 `is_delete = 1`
- ❌ **不要用 `updateById()`** 来修改 `is_delete` 字段

## 修复前后对比

| 对比项 | 修复前（错误） | 修复后（正确） |
|--------|----------------|----------------|
| 方法调用 | `updateById(vehicle)` | `deleteById(id)` |
| 生成的 SQL | `UPDATE SET 所有字段...` | `UPDATE SET is_delete=1` |
| is_delete 是否更新 | ❌ 否（被排除） | ✅ 是 |
| 其他字段是否更新 | ✅ 是（不必要） | ❌ 否（只更新必要字段） |
| 性能 | 差（更新所有字段） | 好（只更新1个字段） |
| 逻辑删除是否生效 | ❌ 否 | ✅ 是 |

## 为什么之前的方案失败

### 尝试1：使用 updateById
```java
vehicle.setIsDelete(1);
vehicleMapper.updateById(vehicle);
```
**失败原因**：`@TableLogic` 会排除 `is_delete` 字段

### 尝试2：使用 LambdaUpdateWrapper
```java
LambdaUpdateWrapper<Vehicle> wrapper = new LambdaUpdateWrapper<>();
wrapper.set(Vehicle::getIsDelete, 1);
vehicleMapper.update(null, wrapper);
```
**问题**：代码复杂，而且可能与 `@TableLogic` 冲突

### 最终方案：直接使用 deleteById
```java
vehicleMapper.deleteById(id);
```
**优势**：
- ✅ 代码简洁
- ✅ 利用 MyBatis-Plus 的特性
- ✅ 自动处理逻辑删除
- ✅ 与 `@TableLogic` 完美配合

## 测试步骤

### 1. 重启后端服务
确保新的代码生效。

### 2. 清除浏览器缓存
强制刷新前端页面（Ctrl+F5）。

### 3. 测试删除功能
1. 进入"车辆管理"页面
2. 选择要删除的车辆，例如：`鄂A 66662`
3. 点击"删除"按钮
4. 确认删除

### 4. 观察后端日志
**修复后应该看到**：
```
[VEHICLE-DELETE] 开始逻辑删除车辆，ID: 3
[VEHICLE-DELETE] 车辆信息 - 车牌号: 鄂A 66662, 当前 is_delete: 0
UPDATE vehicle SET is_delete=1 WHERE id=3
[VEHICLE-DELETE] ✅ 逻辑删除成功，车牌号: 鄂A 66662, 操作人ID: 11
```

**关键点**：
- ✅ SQL 中只有 `SET is_delete=1`
- ✅ 没有更新其他字段
- ✅ 日志显示"逻辑删除成功"

### 5. 验证前端列表
- ✅ 列表自动刷新
- ✅ 已删除的车辆不再显示
- ✅ 显示"删除成功"提示

### 6. 验证数据库
```sql
-- 查看该记录
SELECT id, license_plate, is_delete, update_time 
FROM vehicle 
WHERE id = 3;

-- 预期结果：
-- id | license_plate | is_delete | update_time
-- 3  | 鄂A 66662     | 1         | 2025-10-29 17:00:00
```

```sql
-- 前端列表查询（不应包含该记录）
SELECT * FROM vehicle WHERE is_delete = 0;
```

## 常见问题排查

### Q1: 删除后前端列表还显示该记录？
**检查**:
1. 后端服务是否重启
2. 浏览器缓存是否清除（Ctrl+F5）
3. 后端日志中 SQL 是否为 `UPDATE SET is_delete=1`
4. 数据库中该记录的 `is_delete` 是否为 `1`

### Q2: 后端日志显示 SQL 中没有 is_delete 字段？
**原因**: 代码没有更新，还在使用 `updateById()`

**解决**: 
1. 确认代码已修改为 `deleteById()`
2. 重新编译：`mvn clean compile`
3. 重启后端服务

### Q3: 删除后报错 "车辆不存在"？
**原因**: 记录已经被删除（`is_delete = 1`），`selectById` 自动过滤了它

**解决**: 正常现象，说明删除成功。前端应该已经刷新列表。

### Q4: 如何查看所有已删除的记录？
**SQL**:
```sql
SELECT * FROM vehicle WHERE is_delete = 1;
```

### Q5: 如何恢复已删除的记录？
**SQL**:
```sql
UPDATE vehicle 
SET is_delete = 0, 
    update_time = NOW() 
WHERE id = 3;
```

## MyBatis-Plus @TableLogic 最佳实践

### ✅ 推荐做法

1. **删除操作**：使用 `deleteById()` 或 `deleteBatchIds()`
   ```java
   vehicleMapper.deleteById(id);  // 逻辑删除
   ```

2. **查询操作**：正常使用，自动过滤已删除记录
   ```java
   vehicleMapper.selectById(id);  // 自动添加 is_delete=0
   ```

3. **更新操作**：不要尝试修改 `is_delete` 字段
   ```java
   vehicle.setLicensePlate("新车牌");
   vehicleMapper.updateById(vehicle);  // is_delete 不会被更新
   ```

### ❌ 避免的做法

1. **不要用 updateById 修改 is_delete**
   ```java
   // ❌ 错误：不会生效
   vehicle.setIsDelete(1);
   vehicleMapper.updateById(vehicle);
   ```

2. **不要在 WHERE 条件中手动添加 is_delete**
   ```java
   // ❌ 多余：@TableLogic 会自动添加
   queryWrapper.eq(Vehicle::getIsDelete, 0);
   ```

3. **不要尝试物理删除**
   ```java
   // ❌ 这仍然是逻辑删除，不是物理删除
   vehicleMapper.deleteById(id);
   ```

## 总结

✅ **最终修复方案**：
- 使用 `vehicleMapper.deleteById(id)` 执行逻辑删除
- 让 MyBatis-Plus 的 `@TableLogic` 自动处理 `is_delete` 字段
- 代码简洁，性能优秀，符合框架设计理念

✅ **核心原理**：
- `@TableLogic` 会在 DELETE 操作时自动转换为 UPDATE
- 不要用 UPDATE 操作来修改逻辑删除标记
- 利用框架特性，而不是绕过它

✅ **用户体验**：
- 删除成功后列表自动刷新
- 已删除的记录立即消失
- 操作简单，性能优秀

🎯 **关键教训**：
当框架提供了特定功能（如 `@TableLogic`）时，应该按照框架的设计意图使用，而不是尝试绕过或覆盖它。`deleteById()` 是正确的删除方式，`updateById()` 不适合修改逻辑删除标记。

