# 绑定车辆部门过滤优化说明 ## 需求背景 在绑定车辆功能中,需要限制用户只能绑定与自己同一分公司的车辆,避免跨分公司绑定,确保车辆管理的规范性。 ## 业务规则 ### 部门层级结构 根据项目规范,部门结构如下: ``` 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 | 实现绑定车辆部门过滤功能 | - |