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

车辆查询部门自动匹配功能说明

功能概述

在车辆查询时,无论用户传入的是分公司ID还是子部门ID(如技术部、业务部),系统都会自动查找其所属的分公司,并返回该分公司的车辆。

业务背景

部门层级结构

100 (总公司)
├── 101 (深圳分公司) ← 车辆归属于分公司
│   ├── 201 (技术部)  ← 用户可能属于子部门
│   ├── 202 (业务部)
│   └── 203 (财务部)
├── 102 (广州分公司)
│   ├── 204 (技术部)
│   ├── 205 (业务部)
│   └── 206 (财务部)
└── 103 (北京分公司)
    ├── 207 (技术部)
    └── 208 (业务部)

问题场景

原有逻辑
- 用户A(技术部,deptId=201)查询车辆
- 系统查询:WHERE dept_id = 201
- 结果:❌ 查不到车辆(因为车辆的dept_id=101,不是201)

期望逻辑
- 用户A(技术部,deptId=201)查询车辆
- 系统自动找到分公司:201 → 101(深圳分公司)
- 系统查询:WHERE dept_id = 101
- 结果:✅ 查到深圳分公司的所有车辆

实现方案

SQL查询逻辑

文件VehicleInfoMapper.xml

<!-- 部门过滤:自动查找传入部门所属的分公司(parent_id=100) -->
<if test="deptId != null">
    and v.dept_id = (
        <!-- 如果传入的就是分公司(parent_id=100),直接返回 -->
        select case 
            when exists(select 1 from sys_dept where dept_id = #{deptId} and parent_id = 100) 
            then #{deptId}
            else (
                <!-- 否则从 ancestors 中查找分公司ID -->
                select d.dept_id
                from sys_dept d
                where d.parent_id = 100
                and FIND_IN_SET(d.dept_id, (
                    select ancestors from sys_dept where dept_id = #{deptId}
                ))
                limit 1
            )
        end
    )
</if>

查询逻辑说明

步骤1:判断传入的deptId是否为分公司

when exists(select 1 from sys_dept where dept_id = #{deptId} and parent_id = 100)
  • 如果传入的 deptIdparent_id = 100,说明传入的就是分公司
  • 直接使用该 deptId 查询

步骤2:如果不是分公司,从ancestors中查找

select d.dept_id
from sys_dept d
where d.parent_id = 100
and FIND_IN_SET(d.dept_id, (
    select ancestors from sys_dept where dept_id = #{deptId}
))

原理
- 每个部门的 ancestors 字段存储了所有上级部门ID(用逗号分隔)
- 例如:技术部(201)的 ancestors 可能是 "100,101,201"
- 使用 FIND_IN_SET 在 ancestors 中查找 parent_id=100 的部门
- 找到的就是分公司ID

使用示例

示例1:传入分公司ID

输入
javascript deptId: 101 // 深圳分公司

SQL执行
sql -- 检查:101 的 parent_id = 100 ✅ -- 直接使用:dept_id = 101 WHERE v.dept_id = 101

结果:✅ 返回深圳分公司的所有车辆

示例2:传入子部门ID

输入
javascript deptId: 201 // 深圳分公司-技术部

数据
sql -- sys_dept 表数据 dept_id | parent_id | ancestors 101 | 100 | 100,101 201 | 101 | 100,101,201

SQL执行
sql -- 1. 检查:201 的 parent_id = 100 ❌ -- 2. 查询 ancestors:select ancestors from sys_dept where dept_id = 201 -- 结果:"100,101,201" -- 3. 在 ancestors 中查找 parent_id=100 的部门 -- FIND_IN_SET(d.dept_id, "100,101,201") and d.parent_id = 100 -- 找到:101 -- 4. 使用:dept_id = 101 WHERE v.dept_id = 101

结果:✅ 返回深圳分公司的所有车辆

示例3:传入孙部门ID

输入
javascript deptId: 301 // 深圳分公司-技术部-开发组

数据
sql -- sys_dept 表数据 dept_id | parent_id | ancestors 101 | 100 | 100,101 201 | 101 | 100,101,201 301 | 201 | 100,101,201,301

SQL执行
sql -- 1. 检查:301 的 parent_id = 100 ❌ -- 2. 查询 ancestors:"100,101,201,301" -- 3. 在 ancestors 中查找 parent_id=100 的部门 -- 找到:101 -- 4. 使用:dept_id = 101 WHERE v.dept_id = 101

结果:✅ 返回深圳分公司的所有车辆

应用场景

场景1:绑定车辆

用户:技术部员工(deptId=201)

// 前端调用
listAvailableVehicles(201, 'GENERAL')

// 后端查询
SELECT * FROM tb_vehicle_info v
WHERE v.dept_id = (
    -- 自动找到分公司:201 → 101
    ...
)

结果:显示深圳分公司的所有车辆

场景2:创建任务

用户:业务部员工(deptId=202)

// 前端调用
listAvailableVehicles(202, 'EMERGENCY')

// 后端查询
SELECT * FROM tb_vehicle_info v
WHERE v.dept_id = (
    -- 自动找到分公司:202 → 101
    ...
)

结果:显示深圳分公司的所有车辆

数据流程图

sequenceDiagram
    participant User as 用户(技术部)
    participant Page as 前端页面
    participant API as 后端API
    participant DB as 数据库
    
    User->>Page: 打开绑定车辆页面
    Page->>Page: 获取用户部门ID: 201
    Page->>API: listAvailableVehicles(201, 'GENERAL')
    API->>DB: SELECT dept_id FROM sys_dept WHERE dept_id=201
    DB-->>API: parent_id=101 (不是100)
    API->>DB: SELECT ancestors FROM sys_dept WHERE dept_id=201
    DB-->>API: "100,101,201"
    API->>DB: FIND_IN_SET in ancestors WHERE parent_id=100
    DB-->>API: 找到分公司ID: 101
    API->>DB: SELECT * FROM tb_vehicle_info WHERE dept_id=101
    DB-->>API: 深圳分公司的车辆列表
    API-->>Page: 返回车辆数据
    Page-->>User: 显示深圳分公司的车辆

兼容性

✅ 支持的场景

输入deptId 部门类型 查询结果
101 分公司 101的车辆 ✅
201 子部门 101的车辆 ✅
301 孙部门 101的车辆 ✅
401 曾孙部门 101的车辆 ✅

⚠️ 特殊情况

输入deptId 说明 查询结果
100 总公司 ❌ 查不到(车辆必须归属于分公司)
null 空值 ❌ 不执行部门过滤

性能优化

索引建议

为了提高查询性能,建议在 sys_dept 表添加索引:

-- 部门表索引
CREATE INDEX idx_parent_id ON sys_dept(parent_id);
CREATE INDEX idx_ancestors ON sys_dept(ancestors(100));

-- 车辆表索引(已存在)
CREATE INDEX idx_dept_id ON tb_vehicle_info(dept_id);

查询优化

  • ✅ 使用 CASE WHEN 减少子查询次数
  • ✅ 使用 LIMIT 1 限制结果集
  • FIND_IN_SET 利用索引查询

测试验证

测试场景1:分公司用户

-- 模拟输入:deptId = 101
SELECT * FROM tb_vehicle_info v
WHERE v.dept_id = (
    select case 
        when exists(select 1 from sys_dept where dept_id = 101 and parent_id = 100) 
        then 101
        else (...)
    end
)
-- 结果:dept_id = 101

测试场景2:子部门用户

-- 模拟输入:deptId = 201
SELECT * FROM tb_vehicle_info v
WHERE v.dept_id = (
    select case 
        when exists(select 1 from sys_dept where dept_id = 201 and parent_id = 100) 
        then 201
        else (
            select d.dept_id
            from sys_dept d
            where d.parent_id = 100
            and FIND_IN_SET(d.dept_id, (
                select ancestors from sys_dept where dept_id = 201
            ))
            limit 1
        )
    end
)
-- 结果:dept_id = 101

影响范围

修改的文件

影响的功能

  1. ✅ 绑定车辆 - 自动显示分公司车辆
  2. ✅ 创建任务 - 自动显示分公司车辆
  3. ✅ 车辆管理 - 自动按分公司过滤
  4. ✅ 所有调用 selectVehicleInfoList 的地方

向后兼容

  • ✅ 传入分公司ID仍然正常工作
  • ✅ 传入子部门ID自动转换为分公司ID
  • ✅ 不影响其他查询条件

总结

✅ 优势

  1. 自动匹配 - 无论用户在哪个层级的部门,都能看到分公司的车辆
  2. 用户友好 - 用户不需要关心自己是在哪个子部门
  3. 数据隔离 - 依然保持分公司之间的数据隔离
  4. 向后兼容 - 不影响现有功能

📝 注意事项

  1. ancestors字段 - 依赖 sys_dept 表的 ancestors 字段正确维护
  2. 性能 - 建议添加索引以提高查询性能
  3. 总公司 - 传入总公司ID(100)不会返回车辆

更新时间: 2025-10-25
版本: v1.0
状态: ✅ 已实现