🐛 运单列表按账号项目收口,避免查看其他账号数据

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-29 22:44:02 +08:00
parent 717f50a52c
commit fc1dcb84cc
2 changed files with 101 additions and 33 deletions
@@ -152,7 +152,8 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
@Override
public WaybillVO detail(Long id) {
WaybillVO result = WaybillWrapper.build().entityVO(syncDriverAcceptState(loadEditable(id, false)));
Waybill loaded = syncDriverAcceptState(loadEditable(id, false));
WaybillVO result = WaybillWrapper.build().entityVO(loaded);
fillCustomerNameFromContract(result);
fillCarrierContractName(List.of(result));
fillMileageMaintainable(List.of(result));
@@ -1328,21 +1329,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
LambdaQueryWrapper<Waybill> queryWrapper;
if (Func.isNotEmpty(idList)) {
queryWrapper = Wrappers.<Waybill>lambdaQuery().eq(Waybill::getIsDeleted, 0);
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(
waybill.getAllDept(), waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.and(wrapper -> {
if (deptIdList.size() == 1) {
wrapper.eq(Waybill::getDeptId, deptIdList.get(0));
} else {
wrapper.in(Waybill::getDeptId, deptIdList);
}
TransportBusinessSupport.orProjectVisibleByBusinessDept(wrapper, deptIdList);
});
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_WAYBILL_MANAGE)) {
queryWrapper.eq(Waybill::getCreateUser, AuthUtil.getUserId());
}
applyAccountScope(queryWrapper, waybill.getAllDept(), waybill.getDeptId());
queryWrapper.in(Waybill::getId, idList).orderByDesc(Waybill::getCreateTime);
} else {
queryWrapper = buildQuery(waybill);
@@ -1400,7 +1387,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
@Override
@Transactional(rollbackFor = Exception.class)
public WaybillVO copy(Long id) {
Waybill source = loadEditable(id, true);
Waybill source = loadEditable(id, false);
Waybill target = new Waybill();
target.setProjectId(source.getProjectId());
target.setProjectName(source.getProjectName());
@@ -1820,25 +1807,57 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
return loadingManageService.detail(loadingManage.getId());
}
private LambdaQueryWrapper<Waybill> buildQuery(WaybillVO waybill) {
LambdaQueryWrapper<Waybill> queryWrapper = Wrappers.<Waybill>lambdaQuery().eq(Waybill::getIsDeleted, 0);
// 与常用线路等模块一致:超管默认不按组织过滤;普通用户 allDept!=1 时强制当前组织
// 同时放行「所属项目业务部门」落在可见组织内的数据
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(
waybill.getAllDept(), waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.and(wrapper -> {
if (deptIdList.size() == 1) {
wrapper.eq(Waybill::getDeptId, deptIdList.get(0));
} else {
wrapper.in(Waybill::getDeptId, deptIdList);
}
TransportBusinessSupport.orProjectVisibleByBusinessDept(wrapper, deptIdList);
});
}
/**
* 运单可见范围:超管不限制;普通账号仅本组织运单,以及归属本账号项目的运单。
* 本人创建权限仍只看自己创建的运单。
*/
private void applyAccountScope(LambdaQueryWrapper<Waybill> queryWrapper, Integer allDept, Long deptId) {
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_WAYBILL_MANAGE)) {
queryWrapper.eq(Waybill::getCreateUser, AuthUtil.getUserId());
return;
}
List<Long> deptIdList = TransportBusinessSupport.resolveScopedDeptFilterIds(
allDept, deptId, "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (Func.isEmpty(deptIdList)) {
return;
}
Long currentUserId = AuthUtil.getUserId();
queryWrapper.and(wrapper -> {
if (deptIdList.size() == 1) {
wrapper.eq(Waybill::getDeptId, deptIdList.get(0));
} else {
wrapper.in(Waybill::getDeptId, deptIdList);
}
TransportBusinessSupport.orProjectVisibleByBusinessDept(wrapper, deptIdList);
TransportBusinessSupport.orProjectVisibleByAccount(wrapper, deptIdList, currentUserId);
if (Func.isNotEmpty(currentUserId) && currentUserId > 0) {
wrapper.or().eq(Waybill::getCreateUser, currentUserId);
}
});
}
/**
* 详情、复制等按主键访问时复用列表可见范围,避免越权读取其他账号运单。
*/
private void assertWaybillVisible(Waybill waybill) {
if (waybill == null || Func.isEmpty(waybill.getId())) {
throw new ServiceException("运单管理不存在");
}
if (TransportBusinessSupport.isAdministrator()) {
return;
}
WaybillVO probe = new WaybillVO();
probe.setAllDept(0);
LambdaQueryWrapper<Waybill> wrapper = buildQuery(probe);
wrapper.eq(Waybill::getId, waybill.getId());
if (count(wrapper) <= 0) {
throw new ServiceException("无权查看该运单");
}
}
private LambdaQueryWrapper<Waybill> buildQuery(WaybillVO waybill) {
LambdaQueryWrapper<Waybill> queryWrapper = Wrappers.<Waybill>lambdaQuery().eq(Waybill::getIsDeleted, 0);
applyAccountScope(queryWrapper, waybill.getAllDept(), waybill.getDeptId());
if (Func.isNotEmpty(waybill.getWaybillNo())) {
queryWrapper.eq(Waybill::getWaybillNo, waybill.getWaybillNo());
}
@@ -2259,6 +2278,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
}
assertWaybillVisible(waybill);
return waybill;
}
@@ -286,6 +286,54 @@ public final class TransportBusinessSupport {
return count != null && count > 0;
}
/**
* 列表实际使用的组织范围。
* <p>
* 角色菜单未单独收窄、解析结果为「全部组织」时,超管仍不限制;
* 普通用户在未显式请求全部组织(allDept != 1)时收口到当前组织,
* 避免账号看到其他组织的业务数据。本人创建权限不按组织收口。
*/
public static List<Long> resolveScopedDeptFilterIds(Integer allDept, Long deptId, String moduleName, String menuCode) {
List<Long> deptIdList = resolveDeptFilterIds(allDept, deptId, moduleName, menuCode);
if (Func.isNotEmpty(deptIdList) || isAdministrator() || isOwnCreateScope(menuCode) || Objects.equals(allDept, 1)) {
return deptIdList;
}
return Collections.singletonList(currentDeptId(moduleName));
}
/**
* 账号项目可见范围:承办部门命中指定组织,或当前用户是项目创建人、经办人、负责人。
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public static void orProjectVisibleByAccount(AbstractWrapper<?, ?, ?> wrapper, Collection<Long> deptIds, Long userId) {
if (wrapper == null) {
return;
}
List<Long> ids = deptIds == null ? List.of() : deptIds.stream()
.filter(Objects::nonNull)
.filter(id -> id > 0)
.distinct()
.toList();
if (!ids.isEmpty()) {
if (ids.size() == 1) {
wrapper.or().apply(
"project_id IN (SELECT id FROM blade_project_apply WHERE is_deleted = 0 AND FIND_IN_SET({0}, IFNULL(undertake_dept_id,'')))",
String.valueOf(ids.get(0)));
} else {
String undertakeSql = ids.stream()
.map(id -> "FIND_IN_SET(" + id + ", IFNULL(undertake_dept_id,''))")
.collect(Collectors.joining(" OR "));
wrapper.or().apply(
"project_id IN (SELECT id FROM blade_project_apply WHERE is_deleted = 0 AND (" + undertakeSql + "))");
}
}
if (Func.isNotEmpty(userId) && userId > 0) {
wrapper.or().apply(
"project_id IN (SELECT id FROM blade_project_apply WHERE is_deleted = 0 AND (create_user = {0} OR handler_user_id = {0} OR principal_user_id = {0}))",
userId);
}
}
/**
* 解析列表组织筛选 ID。
*