diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java index cd6ebbe6..0e10fbf3 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/WaybillServiceImpl.java @@ -152,7 +152,8 @@ public class WaybillServiceImpl extends BaseServiceImpl @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 LambdaQueryWrapper queryWrapper; if (Func.isNotEmpty(idList)) { queryWrapper = Wrappers.lambdaQuery().eq(Waybill::getIsDeleted, 0); - List 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 @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 return loadingManageService.detail(loadingManage.getId()); } - private LambdaQueryWrapper buildQuery(WaybillVO waybill) { - LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery().eq(Waybill::getIsDeleted, 0); - // 与常用线路等模块一致:超管默认不按组织过滤;普通用户 allDept!=1 时强制当前组织 - // 同时放行「所属项目业务部门」落在可见组织内的数据 - List 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 queryWrapper, Integer allDept, Long deptId) { if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_WAYBILL_MANAGE)) { queryWrapper.eq(Waybill::getCreateUser, AuthUtil.getUserId()); + return; } + List 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 wrapper = buildQuery(probe); + wrapper.eq(Waybill::getId, waybill.getId()); + if (count(wrapper) <= 0) { + throw new ServiceException("无权查看该运单"); + } + } + + private LambdaQueryWrapper buildQuery(WaybillVO waybill) { + LambdaQueryWrapper queryWrapper = Wrappers.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 if (checkDept) { TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE); } + assertWaybillVisible(waybill); return waybill; } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java index fd4c4547..93f9619d 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/support/TransportBusinessSupport.java @@ -286,6 +286,54 @@ public final class TransportBusinessSupport { return count != null && count > 0; } + /** + * 列表实际使用的组织范围。 + *

+ * 角色菜单未单独收窄、解析结果为「全部组织」时,超管仍不限制; + * 普通用户在未显式请求全部组织(allDept != 1)时收口到当前组织, + * 避免账号看到其他组织的业务数据。本人创建权限不按组织收口。 + */ + public static List resolveScopedDeptFilterIds(Integer allDept, Long deptId, String moduleName, String menuCode) { + List 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 deptIds, Long userId) { + if (wrapper == null) { + return; + } + List 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。 *