# 绑定车辆部门过滤优化说明
## 需求背景
在绑定车辆功能中,需要限制用户只能绑定与自己同一分公司的车辆,避免跨分公司绑定,确保车辆管理的规范性。
## 业务规则
### 部门层级结构
根据项目规范,部门结构如下:
```
100 (总公司)
├── 101 (深圳分公司) ← 用户A所在分公司
│ ├── 201 (技术部)
│ └── 202 (业务部)
├── 102 (广州分公司) ← 用户B所在分公司
│ ├── 203 (技术部)
│ └── 204 (业务部)
└── 103 (北京分公司)
```
### 过滤规则
- ✅ **用户A(深圳分公司)**:只能看到和绑定深圳分公司(deptId=101)的车辆
- ✅ **用户B(广州分公司)**:只能看到和绑定广州分公司(deptId=102)的车辆
- ❌ **跨分公司绑定**:用户A不能绑定广州分公司的车辆
## 问题分析
### 原有实现
**文件:** [`app/pages/bind-vehicle.vue`](file://d:\project\急救转运\code\Api\RuoYi-Vue-master\app\pages\bind-vehicle.vue)
**问题点:**
```javascript
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: "北京分公司"
}
```
**假设:北京分公司目前没有车辆**
**显示结果:**
```
车牌号下拉列表:
- 请选择车牌号 (空列表)
提示信息:当前分公司暂无可用车辆
```
## 数据流程图
```mermaid
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`](file://d:\project\急救转运\code\Api\RuoYi-Vue-master\app\api\vehicle.js)
```javascript
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. 后端验证(建议)
**建议在后端接口中增加以下验证:**
```java
// 验证请求的 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. **正常流程**
- ✅ 用户有部门信息,能正常加载本分公司的车辆
- ✅ 车辆列表只包含本分公司的车辆
2. **边界情况**
- ✅ 用户无部门信息,显示友好提示
- ✅ 分公司无车辆,显示"暂无可用车辆"
- ✅ 网络异常,显示"加载失败"
3. **跨分公司验证**
- ✅ 深圳分公司用户看不到广州分公司的车辆
- ✅ 广州分公司用户看不到深圳分公司的车辆
### 数据验证
```javascript
// 测试用例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`](file://d:\project\急救转运\code\Api\RuoYi-Vue-master\app\pages\bind-vehicle.vue) - 绑定车辆页面
### 依赖的文件
1. [`app/api/vehicle.js`](file://d:\project\急救转运\code\Api\RuoYi-Vue-master\app\api\vehicle.js) - 车辆 API
2. [`app/store/modules/user.js`](file://d:\project\急救转运\code\Api\RuoYi-Vue-master\app\store\modules\user.js) - Vuex 用户状态
### 相关文档
- [车辆管理部门过滤说明.md](./车辆管理部门过滤说明.md)
- [车辆绑定解绑功能说明.md](./车辆绑定解绑功能说明.md)
- [Vuex用户状态完整性优化.md](./Vuex用户状态完整性优化.md)
## 后续优化建议
### 1. 显示部门信息
在车辆列表中显示车辆所属分公司:
```vue
{{ selectedVehiclePlate }} ({{ currentUser.deptName }})
请选择车牌号
```
### 2. 车辆详细信息
选择车辆后显示车辆详细信息:
```vue
车辆类型:{{ selectedVehicleType }}
车辆品牌:{{ selectedVehicleBrand }}
车辆型号:{{ selectedVehicleModel }}
```
### 3. 缓存优化
缓存车辆列表,避免重复请求:
```javascript
// 使用本地缓存
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 | 实现绑定车辆部门过滤功能 | - |