初步实现数据权限过滤
This commit is contained in:
332
基于手机号的数据权限过滤功能说明.md
Normal file
332
基于手机号的数据权限过滤功能说明.md
Normal file
@@ -0,0 +1,332 @@
|
||||
# 基于手机号的数据权限过滤功能实现说明
|
||||
|
||||
## 功能概述
|
||||
|
||||
实现了基于登录用户手机号的数据权限过滤功能,保护用户隐私,确保普通用户只能查看与自己相关的装车订单和运送清单。
|
||||
|
||||
## 核心需求
|
||||
|
||||
1. **角色识别**:系统识别四个角色的手机号
|
||||
- 司机(Driver)
|
||||
- 供应商(Supplier,可能有多个)
|
||||
- 资金方(Fund)
|
||||
- 采购商(Buyer)
|
||||
|
||||
2. **权限规则**:
|
||||
- **超级管理员(roleId=1)**:可以查看所有订单和运送清单
|
||||
- **普通用户**:只能查看与自己手机号相关的数据(四个角色中任意一个匹配即可)
|
||||
|
||||
## 技术实现
|
||||
|
||||
### 1. 后端修改
|
||||
|
||||
#### 1.1 实体类修改
|
||||
|
||||
**文件**: `tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/entity/Delivery.java`
|
||||
|
||||
添加了三个新的手机号字段(司机手机号已存在):
|
||||
|
||||
```java
|
||||
/**
|
||||
* 供应商手机号(逗号分隔)
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String supplierMobile;
|
||||
|
||||
/**
|
||||
* 资金方手机号
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String fundMobile;
|
||||
|
||||
/**
|
||||
* 采购商手机号
|
||||
*/
|
||||
@TableField(exist = false)
|
||||
private String buyerMobile;
|
||||
```
|
||||
|
||||
#### 1.2 服务层修改
|
||||
|
||||
**文件**: `tradeCattle/aiotagro-cattle-trade/src/main/java/com/aiotagro/cattletrade/business/service/impl/DeliveryServiceImpl.java`
|
||||
|
||||
修改了两个关键方法:
|
||||
|
||||
##### (1)装车订单列表查询 - `pageQueryListLog`
|
||||
|
||||
```java
|
||||
public PageResultResponse<Delivery> pageQueryListLog(DeliverListDto dto) {
|
||||
// 1. 获取当前登录用户手机号
|
||||
String currentUserMobile = SecurityUtil.getUserMobile();
|
||||
|
||||
// 2. 查询所有数据
|
||||
List<Delivery> resList = list(deliveryLambdaQueryWrapper);
|
||||
|
||||
// 3. 关联查询四个角色的手机号
|
||||
resList.forEach(deliveryLogVo -> {
|
||||
// 查询供应商手机号(supplierId是逗号分隔的)
|
||||
// 查询资金方手机号
|
||||
// 查询采购商手机号
|
||||
// 查询司机手机号
|
||||
});
|
||||
|
||||
// 4. 数据权限过滤(非超级管理员)
|
||||
if (!SecurityUtil.isSuperAdmin()) {
|
||||
resList = resList.stream().filter(delivery -> {
|
||||
// 匹配司机手机号
|
||||
// 匹配供应商手机号
|
||||
// 匹配资金方手机号
|
||||
// 匹配采购商手机号
|
||||
return hasPermission;
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
return new PageResultResponse(filteredTotal, resList);
|
||||
}
|
||||
```
|
||||
|
||||
##### (2)运送清单列表查询 - `pageQuery`
|
||||
|
||||
实现逻辑与 `pageQueryListLog` 相同,确保两个页面的权限过滤一致。
|
||||
|
||||
### 2. 权限判断逻辑
|
||||
|
||||
#### 2.1 超级管理员判断
|
||||
|
||||
```java
|
||||
// 位置:SecurityUtil.java
|
||||
public static boolean isSuperAdmin() {
|
||||
Integer roleId = getRoleId();
|
||||
return roleId != null && roleId.equals(RoleConstants.SUPER_ADMIN_ROLE_ID);
|
||||
}
|
||||
|
||||
// RoleConstants.SUPER_ADMIN_ROLE_ID = 1
|
||||
```
|
||||
|
||||
#### 2.2 手机号匹配逻辑
|
||||
|
||||
普通用户的数据访问权限判断:
|
||||
|
||||
```java
|
||||
boolean hasPermission = false;
|
||||
|
||||
// 1. 检查是否是司机
|
||||
if (currentUserMobile.equals(delivery.getDriverMobile())) {
|
||||
hasPermission = true;
|
||||
}
|
||||
|
||||
// 2. 检查是否是供应商(可能有多个)
|
||||
if (!hasPermission && delivery.getSupplierMobile() != null) {
|
||||
String[] supplierMobiles = delivery.getSupplierMobile().split(",");
|
||||
for (String mobile : supplierMobiles) {
|
||||
if (currentUserMobile.equals(mobile.trim())) {
|
||||
hasPermission = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 检查是否是资金方
|
||||
if (!hasPermission && currentUserMobile.equals(delivery.getFundMobile())) {
|
||||
hasPermission = true;
|
||||
}
|
||||
|
||||
// 4. 检查是否是采购商
|
||||
if (!hasPermission && currentUserMobile.equals(delivery.getBuyerMobile())) {
|
||||
hasPermission = true;
|
||||
}
|
||||
|
||||
return hasPermission;
|
||||
```
|
||||
|
||||
### 3. 角色手机号关联查询
|
||||
|
||||
系统在返回列表数据前,会根据角色ID查询对应的手机号:
|
||||
|
||||
```java
|
||||
// 1. 供应商手机号查询(supplierId可能是逗号分隔的多个ID)
|
||||
if (delivery.getSupplierId() != null) {
|
||||
String[] supplierIds = delivery.getSupplierId().split(",");
|
||||
List<String> supplierMobiles = new ArrayList<>();
|
||||
for (String supplierId : supplierIds) {
|
||||
Member supplier = memberMapper.selectById(Integer.parseInt(supplierId));
|
||||
if (supplier != null) {
|
||||
supplierMobiles.add(supplier.getMobile());
|
||||
}
|
||||
}
|
||||
delivery.setSupplierMobile(String.join(",", supplierMobiles));
|
||||
}
|
||||
|
||||
// 2. 资金方手机号查询
|
||||
if (delivery.getFundId() != null) {
|
||||
Member fund = memberMapper.selectById(delivery.getFundId());
|
||||
if (fund != null) {
|
||||
delivery.setFundMobile(fund.getMobile());
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 采购商手机号查询
|
||||
if (delivery.getBuyerId() != null) {
|
||||
Member buyer = memberMapper.selectById(delivery.getBuyerId());
|
||||
if (buyer != null) {
|
||||
delivery.setBuyerMobile(buyer.getMobile());
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 司机手机号查询
|
||||
if (delivery.getDriverId() != null) {
|
||||
Map<String, Object> driverInfo = memberDriverMapper.selectDriverById(delivery.getDriverId());
|
||||
if (driverInfo != null) {
|
||||
delivery.setDriverMobile((String) driverInfo.get("mobile"));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 影响范围
|
||||
|
||||
### 1. 受影响的页面
|
||||
|
||||
- **装车订单列表页** (`/shipping/loadingOrder`)
|
||||
- 接口:`/delivery/pageDeliveryOrderList`
|
||||
|
||||
- **运送清单列表页** (`/entry/attestation`)
|
||||
- 接口:`/delivery/pageQueryList`
|
||||
|
||||
### 2. 不受影响的功能
|
||||
|
||||
- 订单详情查询
|
||||
- 订单创建/编辑
|
||||
- 设备分配
|
||||
- 其他管理功能
|
||||
|
||||
## 测试建议
|
||||
|
||||
### 测试场景1:超级管理员登录
|
||||
|
||||
**步骤**:
|
||||
1. 使用 roleId=1 的超级管理员账号登录
|
||||
2. 访问装车订单列表页
|
||||
3. 访问运送清单列表页
|
||||
|
||||
**预期结果**:
|
||||
- 可以看到系统中所有的订单和运送清单
|
||||
- 控制台输出:`=== 超级管理员,不执行数据权限过滤 ===`
|
||||
|
||||
### 测试场景2:司机登录
|
||||
|
||||
**步骤**:
|
||||
1. 使用司机账号登录(手机号:13800138001)
|
||||
2. 访问装车订单列表页
|
||||
3. 访问运送清单列表页
|
||||
|
||||
**预期结果**:
|
||||
- 只能看到 driverMobile = '13800138001' 的订单
|
||||
- 其他订单不可见
|
||||
- 控制台输出匹配日志
|
||||
|
||||
### 测试场景3:供应商登录
|
||||
|
||||
**步骤**:
|
||||
1. 使用供应商账号登录(手机号:13900139001)
|
||||
2. 访问装车订单列表页
|
||||
3. 访问运送清单列表页
|
||||
|
||||
**预期结果**:
|
||||
- 只能看到 supplierMobile 包含 '13900139001' 的订单
|
||||
- 其他订单不可见
|
||||
|
||||
### 测试场景4:资金方/采购商登录
|
||||
|
||||
**步骤**:
|
||||
1. 使用资金方或采购商账号登录
|
||||
2. 访问装车订单列表页
|
||||
3. 访问运送清单列表页
|
||||
|
||||
**预期结果**:
|
||||
- 只能看到与自己手机号匹配的订单
|
||||
- 其他订单不可见
|
||||
|
||||
### 测试场景5:多角色用户
|
||||
|
||||
**步骤**:
|
||||
1. 创建一个订单,将同一个用户设置为多个角色(如既是司机又是供应商)
|
||||
2. 使用该用户登录
|
||||
3. 访问列表页
|
||||
|
||||
**预期结果**:
|
||||
- 该用户可以看到此订单(任意角色匹配即可)
|
||||
|
||||
## 调试信息
|
||||
|
||||
系统在控制台输出详细的调试日志,方便问题排查:
|
||||
|
||||
```
|
||||
=== 装车订单列表查询 - 当前登录用户手机号: 13800138001 ===
|
||||
=== 非超级管理员,执行数据权限过滤 ===
|
||||
当前用户手机号: 13800138001
|
||||
订单 ZC20251016142501 - 匹配司机手机号
|
||||
订单 ZC20251016142502 - 无权限,过滤掉
|
||||
过滤后的订单数量: 1
|
||||
```
|
||||
|
||||
## 数据库表结构
|
||||
|
||||
### delivery 表
|
||||
|
||||
相关字段:
|
||||
- `supplier_id` VARCHAR - 供应商ID(逗号分隔)
|
||||
- `fund_id` INT - 资金方ID
|
||||
- `driver_id` INT - 司机ID
|
||||
- `buyer_id` INT - 采购商ID
|
||||
|
||||
### member 表
|
||||
|
||||
相关字段:
|
||||
- `id` INT - 会员ID
|
||||
- `mobile` VARCHAR - 手机号
|
||||
- `type` INT - 会员类型
|
||||
|
||||
### member_driver 表
|
||||
|
||||
相关字段:
|
||||
- `id` INT - 司机ID
|
||||
- `member_id` INT - 关联 member.id
|
||||
- `username` VARCHAR - 司机姓名
|
||||
- `mobile` VARCHAR - 司机手机号
|
||||
|
||||
## 性能优化建议
|
||||
|
||||
1. **批量查询优化**:当前实现对每个订单单独查询角色手机号,如果订单数量较多,建议改为批量查询:
|
||||
```java
|
||||
// 收集所有角色ID
|
||||
List<Integer> allMemberIds = new ArrayList<>();
|
||||
// ... 添加所有 fundId, buyerId 等
|
||||
|
||||
// 批量查询
|
||||
List<Member> members = memberMapper.selectBatchIds(allMemberIds);
|
||||
Map<Integer, String> idToMobileMap = ...;
|
||||
```
|
||||
|
||||
2. **缓存优化**:对于频繁访问的会员信息,可以考虑添加 Redis 缓存。
|
||||
|
||||
3. **分页问题**:当前实现在内存中过滤数据后更新分页信息,这可能导致分页不准确。建议:
|
||||
- 方案A:在数据库层面进行过滤(通过 SQL JOIN 查询)
|
||||
- 方案B:在应用层正确处理分页逻辑
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **手机号格式**:确保系统中所有手机号格式一致(如:都是11位数字,无空格)
|
||||
2. **空值处理**:代码中已处理 null 值情况,避免空指针异常
|
||||
3. **供应商多值处理**:supplierId 可能是逗号分隔的多个值,需要分别处理
|
||||
4. **调试日志**:生产环境建议关闭或降低日志级别
|
||||
|
||||
## 版本信息
|
||||
|
||||
- 实现日期:2025-10-16
|
||||
- 修改文件:
|
||||
- `Delivery.java`
|
||||
- `DeliveryServiceImpl.java`
|
||||
- 影响接口:
|
||||
- `/delivery/pageDeliveryOrderList` (装车订单列表)
|
||||
- `/delivery/pageQueryList` (运送清单列表)
|
||||
|
||||
Reference in New Issue
Block a user