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

绑定车辆部门过滤优化说明

需求背景

在绑定车辆功能中,需要限制用户只能绑定与自己同一分公司的车辆,避免跨分公司绑定,确保车辆管理的规范性。

业务规则

部门层级结构

根据项目规范,部门结构如下:

100 (总公司)
├── 101 (深圳分公司) ← 用户A所在分公司
│   ├── 201 (技术部)
│   └── 202 (业务部)
├── 102 (广州分公司) ← 用户B所在分公司
│   ├── 203 (技术部)
│   └── 204 (业务部)
└── 103 (北京分公司)

过滤规则

  • 用户A(深圳分公司):只能看到和绑定深圳分公司(deptId=101)的车辆
  • 用户B(广州分公司):只能看到和绑定广州分公司(deptId=102)的车辆
  • 跨分公司绑定:用户A不能绑定广州分公司的车辆

问题分析

原有实现

文件: app/pages/bind-vehicle.vue

问题点:

loadVehicleList() {
  this.loading = true
  // ❌ 使用默认值 100(总公司),加载所有车辆
  const deptId = this.currentUser.deptId || 100
  listAvailableVehicles(deptId, 'GENERAL').then(response => {
    // ...
  })
}

存在的问题:
1. ❌ 当用户的 deptId 为空时,默认使用 100(总公司),会加载所有分公司的车辆
2. ❌ 没有验证 deptId 是否存在
3. ❌ 用户可能看到其他分公司的车辆
4. ❌ 缺少友好的提示信息

解决方案

核心改进

修改前:
javascript loadVehicleList() { this.loading = true const deptId = this.currentUser.deptId || 100 // ❌ 使用默认值 listAvailableVehicles(deptId, 'GENERAL').then(response => { const vehicleList = response.data || response.rows || [] this.vehicleOptions = vehicleList.map(vehicle => ({ id: vehicle.vehicleId, name: vehicle.vehicleNo, // ... })) this.vehiclePlates = this.vehicleOptions.map(v => v.name) }).catch(error => { this.loading = false console.error('加载车辆列表失败:', error) this.$modal.showToast('加载车辆列表失败') }) }

修改后:
```javascript
loadVehicleList() {
this.loading = true
// ✅ 获取当前用户的部门ID
const deptId = this.currentUser.deptId

// ✅ 验证部门ID是否存在
if (!deptId) {
this.loading = false
console.error('无法获取用户部门信息')
this.$modal.showToast('无法获取用户部门信息')
return
}

// ✅ 使用用户所在的部门ID加载车辆列表
listAvailableVehicles(deptId, 'GENERAL').then(response => {
this.loading = false
const vehicleList = response.data || response.rows || []

// ✅ 提示无可用车辆
if (vehicleList.length === 0) {
  console.log('当前分公司暂无可用车辆')
  this.$modal.showToast('当前分公司暂无可用车辆')
}

this.vehicleOptions = vehicleList.map(vehicle => ({
  id: vehicle.vehicleId,
  name: vehicle.vehicleNo,
  type: vehicle.vehicleType,
  brand: vehicle.vehicleBrand,
  model: vehicle.vehicleModel
}))
this.vehiclePlates = this.vehicleOptions.map(v => v.name)

// ✅ 记录日志
console.log(`加载了 ${vehicleList.length} 辆车辆(部门ID: ${deptId})`)

}).catch(error => {
this.loading = false
console.error('加载车辆列表失败:', error)
this.$modal.showToast('加载车辆列表失败')
})
}
```

改进点

  1. 严格验证:检查 deptId 是否存在,不使用默认值
  2. 友好提示:当无部门信息或无可用车辆时,给出明确提示
  3. 日志记录:记录加载的车辆数量和部门ID,便于调试
  4. 规范管理:确保用户只能看到本分公司的车辆

实现效果

场景1:深圳分公司用户绑定车辆

用户信息:
javascript { userId: 1, userName: "张三", deptId: 101, // 深圳分公司 deptName: "深圳分公司" }

操作流程:
1. 用户张三打开绑定车辆页面
2. 系统使用 deptId: 101 查询车辆
3. 只返回深圳分公司的车辆列表

显示结果:
```
车牌号下拉列表:
- 粤A12345 (深圳分公司)
- 粤A67890 (深圳分公司)
- 粤A11111 (深圳分公司)

❌ 不显示:
- 粤B12345 (广州分公司)
- 京A12345 (北京分公司)
```

场景2:广州分公司用户绑定车辆

用户信息:
javascript { userId: 2, userName: "李四", deptId: 102, // 广州分公司 deptName: "广州分公司" }

显示结果:
```
车牌号下拉列表:
- 粤B12345 (广州分公司)
- 粤B67890 (广州分公司)

❌ 不显示:
- 粤A12345 (深圳分公司)
- 京A12345 (北京分公司)
```

场景3:用户无部门信息

用户信息:
javascript { userId: 3, userName: "王五", deptId: null // 无部门信息 }

操作流程:
1. 用户王五打开绑定车辆页面
2. 系统检测到 deptId 为空
3. 显示提示:"无法获取用户部门信息"
4. 不加载任何车辆

显示结果:
```
车牌号下拉列表:
- 请选择车牌号 (空列表,无法选择)

提示信息:无法获取用户部门信息
```

场景4:分公司暂无车辆

用户信息:
javascript { userId: 4, userName: "赵六", deptId: 103, // 北京分公司 deptName: "北京分公司" }

假设:北京分公司目前没有车辆

显示结果:
```
车牌号下拉列表:
- 请选择车牌号 (空列表)

提示信息:当前分公司暂无可用车辆
```

数据流程图

sequenceDiagram
    participant User as 用户
    participant Page as 绑定车辆页面
    participant Vuex as Vuex Store
    participant API as 后端API
    
    User->>Page: 打开绑定车辆页面
    Page->>Vuex: 获取 currentUser.deptId
    
    alt deptId 存在
        Vuex-->>Page: 返回 deptId: 101
        Page->>API: listAvailableVehicles(101, 'GENERAL')
        
        alt 有车辆数据
            API-->>Page: 返回深圳分公司的车辆列表
            Page-->>User: 显示车辆列表
        else 无车辆数据
            API-->>Page: 返回空列表
            Page-->>User: 提示:当前分公司暂无可用车辆
        end
    else deptId 不存在
        Vuex-->>Page: 返回 null
        Page-->>User: 提示:无法获取用户部门信息
    end

后端接口说明

listAvailableVehicles API

文件: app/api/vehicle.js

export function listAvailableVehicles(deptId, taskType) {
  return request({
    url: '/task/vehicle/available',
    method: 'get',
    params: {
      deptId: deptId,      // 部门ID,用于过滤车辆
      taskType: taskType    // 任务类型
    }
  })
}

后端接口: /task/vehicle/available

请求示例:
GET /task/vehicle/available?deptId=101&taskType=GENERAL

响应示例:
json { "code": 200, "msg": "查询成功", "data": [ { "vehicleId": 1, "vehicleNo": "粤A12345", "vehicleType": "救护车", "vehicleBrand": "福特", "vehicleModel": "全顺", "deptId": 101, "deptName": "深圳分公司" }, { "vehicleId": 2, "vehicleNo": "粤A67890", "vehicleType": "救护车", "vehicleBrand": "丰田", "vehicleModel": "海狮", "deptId": 101, "deptName": "深圳分公司" } ] }

无车辆时:
json { "code": 200, "msg": "查询成功", "data": [] }

安全性与权限

1. 前端验证

  • ✅ 检查用户是否有部门信息
  • ✅ 使用用户实际的 deptId,不使用默认值
  • ✅ 空列表时给出友好提示

2. 后端验证(建议)

建议在后端接口中增加以下验证:

// 验证请求的 deptId 是否与用户的 deptId 一致
@GetMapping("/available")
public AjaxResult listAvailableVehicles(@RequestParam Long deptId, @RequestParam String taskType) {
    LoginUser loginUser = SecurityUtils.getLoginUser();
    Long userDeptId = loginUser.getUser().getDeptId();
    
    // ✅ 验证部门权限
    if (!deptId.equals(userDeptId)) {
        return error("无权查询其他部门的车辆");
    }
    
    // 查询车辆列表...
}

3. 数据隔离

  • 前端隔离:只显示本分公司的车辆
  • 后端隔离:接口层面验证部门权限
  • 数据库隔离:查询时加上部门条件

测试要点

功能测试

  1. 正常流程
  • ✅ 用户有部门信息,能正常加载本分公司的车辆
  • ✅ 车辆列表只包含本分公司的车辆
  1. 边界情况
  • ✅ 用户无部门信息,显示友好提示
  • ✅ 分公司无车辆,显示"暂无可用车辆"
  • ✅ 网络异常,显示"加载失败"
  1. 跨分公司验证
  • ✅ 深圳分公司用户看不到广州分公司的车辆
  • ✅ 广州分公司用户看不到深圳分公司的车辆

数据验证

// 测试用例1:深圳分公司用户
{
  userId: 1,
  deptId: 101,
  expectedVehicles: ['粤A12345', '粤A67890'],  // 只有深圳的车
  notExpectedVehicles: ['粤B12345', '京A12345']  // 不应包含其他分公司
}

// 测试用例2:广州分公司用户
{
  userId: 2,
  deptId: 102,
  expectedVehicles: ['粤B12345', '粤B67890'],  // 只有广州的车
  notExpectedVehicles: ['粤A12345', '京A12345']  // 不应包含其他分公司
}

日志示例

正常加载

控制台输出:
加载了 3 辆车辆(部门ID: 101)

无部门信息

控制台输出:
无法获取用户部门信息

用户看到:
Toast提示:无法获取用户部门信息

无可用车辆

控制台输出:
当前分公司暂无可用车辆

用户看到:
Toast提示:当前分公司暂无可用车辆

相关文件

修改的文件

  1. app/pages/bind-vehicle.vue - 绑定车辆页面

依赖的文件

  1. app/api/vehicle.js - 车辆 API
  2. app/store/modules/user.js - Vuex 用户状态

相关文档

后续优化建议

1. 显示部门信息

在车辆列表中显示车辆所属分公司:

<picker mode="selector" :range="vehiclePlates" @change="onVehiclePlateChange">
  <view class="form-input picker-input">
    <text v-if="selectedVehiclePlate">
      {{ selectedVehiclePlate }} ({{ currentUser.deptName }})
    </text>
    <text v-else>请选择车牌号</text>
    <uni-icons type="arrowright" size="16" color="#999"></uni-icons>
  </view>
</picker>

2. 车辆详细信息

选择车辆后显示车辆详细信息:

<view v-if="selectedVehicleId" class="vehicle-detail">
  <text>车辆类型:{{ selectedVehicleType }}</text>
  <text>车辆品牌:{{ selectedVehicleBrand }}</text>
  <text>车辆型号:{{ selectedVehicleModel }}</text>
</view>

3. 缓存优化

缓存车辆列表,避免重复请求:

// 使用本地缓存
const cacheKey = `vehicle_list_${deptId}`
const cachedData = storage.get(cacheKey)

if (cachedData && Date.now() - cachedData.timestamp < 5 * 60 * 1000) {
  // 5分钟内使用缓存
  this.vehicleOptions = cachedData.data
} else {
  // 请求新数据并缓存
  listAvailableVehicles(deptId, 'GENERAL').then(response => {
    const data = response.data || []
    storage.set(cacheKey, {
      data: data,
      timestamp: Date.now()
    })
  })
}

总结

通过本次优化,实现了以下目标:

  1. 部门隔离:用户只能看到和绑定本分公司的车辆
  2. 严格验证:检查用户部门信息,不使用默认值
  3. 友好提示:各种异常情况都有明确的提示信息
  4. 日志记录:便于调试和问题排查
  5. 安全性:避免跨分公司绑定车辆

核心价值:
- 提高了数据安全性
- 规范了车辆管理流程
- 提升了用户体验
- 便于后续维护和扩展

版本历史

版本 日期 修改内容 修改人

| 1.0 | 2025-10-15 | 实现绑定车辆部门过滤功能 | - |