✨ 角色菜单数据权限替代用户级组织/客商权限

将后台数据权限迁移到角色×菜单配置,运行时按 menuCode 解析;组织树取最宽合并;停用用户侧组织与客商授权过滤。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-27 04:05:01 +08:00
parent 18065d6ae4
commit 8737c4b5f9
34 changed files with 1640 additions and 160 deletions
@@ -36,6 +36,7 @@ import org.springblade.core.excel.util.ExcelUtil;
import org.springblade.core.mp.support.Condition;
import org.springblade.core.mp.support.Query;
import org.springblade.core.secure.annotation.PreAuth;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.api.R;
import org.springblade.core.tool.utils.DateUtil;
import org.springblade.core.tool.utils.Func;
@@ -162,9 +163,16 @@ public class CommonAddressController extends BladeController {
if (Func.isNotEmpty(ids)) {
queryWrapper.in(CommonAddress::getId, Func.toLongList(ids));
}
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(commonAddress.getAllDept(), commonAddress.getDeptId(), "常用地址");
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(commonAddress.getAllDept(), commonAddress.getDeptId(), "常用地址", TransportBusinessSupport.MENU_COMMON_ADDRESS);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.eq(CommonAddress::getDeptId, deptIdList.get(0));
if (deptIdList.size() == 1) {
queryWrapper.eq(CommonAddress::getDeptId, deptIdList.get(0));
} else {
queryWrapper.in(CommonAddress::getDeptId, deptIdList);
}
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_COMMON_ADDRESS)) {
queryWrapper.eq(CommonAddress::getCreateUser, AuthUtil.getUserId());
}
if (Func.isNotEmpty(commonAddress.getAddressName())) {
queryWrapper.like(CommonAddress::getAddressName, commonAddress.getAddressName().trim());
@@ -79,6 +79,15 @@
<if test="address.deptId != null">
AND dept_id = #{address.deptId}
</if>
<if test="address.deptIds != null and address.deptIds.size() > 0">
AND dept_id IN
<foreach collection="address.deptIds" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</if>
<if test="address.scopeCreateUser != null">
AND create_user = #{address.scopeCreateUser}
</if>
<if test="address.contactName != null and address.contactName != ''">
<bind name="contactNameLike" value="'%' + address.contactName + '%'"/>
AND contact_name LIKE #{contactNameLike}
@@ -167,8 +167,14 @@ public class CommonAddressServiceImpl extends BaseServiceImpl<CommonAddressMappe
}
private void prepareQuery(CommonAddressVO address) {
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(address.getAllDept(), address.getDeptId(), "常用地址");
address.setDeptId(Func.isEmpty(deptIdList) ? null : deptIdList.get(0));
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(address.getAllDept(), address.getDeptId(), "常用地址", TransportBusinessSupport.MENU_COMMON_ADDRESS);
address.setDeptId(null);
address.setDeptIds(Func.isEmpty(deptIdList) ? null : deptIdList);
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_COMMON_ADDRESS)) {
address.setScopeCreateUser(AuthUtil.getUserId());
} else {
address.setScopeCreateUser(null);
}
address.setAddressName(trimToNull(address.getAddressName()));
address.setAddressType(trimToNull(address.getAddressType()));
address.setDetailAddress(trimToNull(address.getDetailAddress()));
@@ -28,6 +28,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springblade.common.excel.ImportFailureException;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.UserCache;
@@ -93,7 +94,7 @@ public class CommonCargoServiceImpl extends BaseServiceImpl<CommonCargoMapper, C
BusinessRemoveResultVO result = new BusinessRemoveResultVO();
List<Long> deleteIdList = new ArrayList<>();
for (CommonCargo commonCargo : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(commonCargo.getDeptId(), "常用货物");
TransportBusinessSupport.assertCurrentDept(commonCargo.getDeptId(), "常用货物", TransportBusinessSupport.MENU_COMMON_CARGO);
if (shouldSkipDelete(commonCargo)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(commonCargo.getCargoCode());
@@ -214,7 +215,7 @@ public class CommonCargoServiceImpl extends BaseServiceImpl<CommonCargoMapper, C
private LambdaQueryWrapper<CommonCargo> buildQuery(CommonCargoVO commonCargo) {
LambdaQueryWrapper<CommonCargo> queryWrapper = Wrappers.<CommonCargo>lambdaQuery().eq(CommonCargo::getIsDeleted, 0);
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(commonCargo.getAllDept(), commonCargo.getDeptId(), "常用货物");
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(commonCargo.getAllDept(), commonCargo.getDeptId(), "常用货物", TransportBusinessSupport.MENU_COMMON_CARGO);
if (Func.isNotEmpty(deptIdList)) {
if (deptIdList.size() == 1) {
queryWrapper.eq(CommonCargo::getDeptId, deptIdList.get(0));
@@ -222,6 +223,9 @@ public class CommonCargoServiceImpl extends BaseServiceImpl<CommonCargoMapper, C
queryWrapper.in(CommonCargo::getDeptId, deptIdList);
}
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_COMMON_CARGO)) {
queryWrapper.eq(CommonCargo::getCreateUser, AuthUtil.getUserId());
}
if (Func.isNotEmpty(commonCargo.getFirstCargoTypeId())) {
queryWrapper.eq(CommonCargo::getFirstCargoTypeId, commonCargo.getFirstCargoTypeId());
}
@@ -355,7 +359,7 @@ public class CommonCargoServiceImpl extends BaseServiceImpl<CommonCargoMapper, C
throw new ServiceException("常用货物不存在");
}
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(commonCargo.getDeptId(), "常用货物");
TransportBusinessSupport.assertCurrentDept(commonCargo.getDeptId(), "常用货物", TransportBusinessSupport.MENU_COMMON_CARGO);
}
return commonCargo;
}
@@ -28,6 +28,7 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springblade.common.excel.ImportFailureException;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.SysCache;
@@ -96,7 +97,7 @@ public class CommonRouteServiceImpl extends BaseServiceImpl<CommonRouteMapper, C
BusinessRemoveResultVO result = new BusinessRemoveResultVO();
List<Long> deleteIdList = new ArrayList<>();
for (CommonRoute commonRoute : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(commonRoute.getDeptId(), "常用线路");
TransportBusinessSupport.assertCurrentDept(commonRoute.getDeptId(), "常用线路", TransportBusinessSupport.MENU_COMMON_ROUTE);
if (shouldSkipDelete(commonRoute)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(commonRoute.getRouteCode());
@@ -158,9 +159,16 @@ public class CommonRouteServiceImpl extends BaseServiceImpl<CommonRouteMapper, C
private LambdaQueryWrapper<CommonRoute> buildQuery(CommonRouteVO commonRoute) {
LambdaQueryWrapper<CommonRoute> queryWrapper = Wrappers.<CommonRoute>lambdaQuery().eq(CommonRoute::getIsDeleted, 0);
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(commonRoute.getAllDept(), commonRoute.getDeptId(), "常用线路");
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(commonRoute.getAllDept(), commonRoute.getDeptId(), "常用线路", TransportBusinessSupport.MENU_COMMON_ROUTE);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.eq(CommonRoute::getDeptId, deptIdList.get(0));
if (deptIdList.size() == 1) {
queryWrapper.eq(CommonRoute::getDeptId, deptIdList.get(0));
} else {
queryWrapper.in(CommonRoute::getDeptId, deptIdList);
}
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_COMMON_ROUTE)) {
queryWrapper.eq(CommonRoute::getCreateUser, AuthUtil.getUserId());
}
if (Func.isNotEmpty(commonRoute.getRouteCode())) {
queryWrapper.eq(CommonRoute::getRouteCode, commonRoute.getRouteCode());
@@ -336,7 +344,7 @@ public class CommonRouteServiceImpl extends BaseServiceImpl<CommonRouteMapper, C
throw new ServiceException("常用线路不存在");
}
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(commonRoute.getDeptId(), "常用线路");
TransportBusinessSupport.assertCurrentDept(commonRoute.getDeptId(), "常用线路", TransportBusinessSupport.MENU_COMMON_ROUTE);
}
return commonRoute;
}
@@ -41,7 +41,6 @@ import org.springblade.core.tenant.annotation.TenantIgnore;
import org.springblade.core.tool.jackson.JsonUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.pojo.entity.UserCustomerScope;
import org.springblade.thirdparty.bfi.constant.BfiConstant;
import org.springblade.thirdparty.bfi.pojo.dto.BfiCustomerQueryDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultErrorVO;
@@ -70,7 +69,6 @@ import org.springblade.transport.mapper.CustomerInvoiceContactMapper;
import org.springblade.transport.mapper.CustomerInvoiceInfoMapper;
import org.springblade.transport.mapper.CustomerReceiptAccountMapper;
import org.springblade.transport.mapper.PaymentApplicationMapper;
import org.springblade.transport.mapper.UserCustomerScopeMapper;
import org.springblade.transport.pojo.entity.CreditRatingStandard;
import org.springblade.transport.pojo.entity.CreditScoreCategory;
import org.springblade.transport.pojo.entity.CreditScoreItem;
@@ -220,7 +218,6 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
private final CreditScoreItemMapper itemMapper;
private final CreditScoreItemOptionMapper optionMapper;
private final CreditRatingStandardMapper standardMapper;
private final UserCustomerScopeMapper userCustomerScopeMapper;
private final PaymentApplicationMapper paymentApplicationMapper;
private final IBfiService bfiService;
private final CustomerBfiConverter customerBfiConverter;
@@ -285,9 +282,7 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
boolean result = saveOrUpdate(entity);
customer.setId(entity.getId());
replaceDetail(entity.getId(), customer);
if (created && result) {
grantNewCustomerToIncludedUsers(entity);
}
// 客商权限已从用户侧移除,新增客商不再写入用户客商授权表。
// 仅「提交」写入变更记录;「保存」不落变更记录。
// 保存后重新读取落库结果再对比,避免前端冗余字段造成误判;无实质变更则不记。
if (result && !created && Boolean.TRUE.equals(customer.getRecordChange())) {
@@ -1769,26 +1764,6 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
}
}
private void grantNewCustomerToIncludedUsers(CustomerArchive customer) {
String tenantId = Func.isNotEmpty(customer.getTenantId()) ? customer.getTenantId() : AuthUtil.getTenantId();
List<Long> userIds = new ArrayList<>(baseMapper.selectIncludeNewCustomerUserIds(tenantId));
Long currentUserId = AuthUtil.getUserId();
if (Func.isNotEmpty(currentUserId) && !userIds.contains(currentUserId)) {
userIds.add(currentUserId);
}
userIds.forEach(userId -> {
Long scopeCount = userCustomerScopeMapper.selectCount(Wrappers.<UserCustomerScope>lambdaQuery()
.eq(UserCustomerScope::getUserId, userId)
.eq(UserCustomerScope::getCustomerId, customer.getId()));
if (scopeCount != null && scopeCount > 0) {
return;
}
UserCustomerScope scope = new UserCustomerScope();
scope.setUserId(userId);
scope.setCustomerId(customer.getId());
userCustomerScopeMapper.insert(scope);
});
}
private String nextCustomerCode() {
CustomerArchive latest = list(Wrappers.<CustomerArchive>lambdaQuery()
@@ -1843,19 +1818,14 @@ public class CustomerArchiveServiceImpl extends BaseServiceImpl<CustomerArchiveM
}
private CustomerArchive ensureCustomerAccessible(Long id) {
CustomerArchive customer = getExistingCustomer(id);
Long userId = currentScopeUserId();
if (userId != null && baseMapper.selectCustomerPermissionCount(id, userId) == 0) {
throw new ServiceException("无权访问该客商档案");
}
return customer;
return getExistingCustomer(id);
}
/**
* 客商可见范围用户。超管返回空,表示不按用户客商授权表过滤。
* 客商可见范围用户。用户侧客商权限已停用,始终返回空表示不过滤。
*/
private Long currentScopeUserId() {
return TransportBusinessSupport.isAdministrator() ? null : AuthUtil.getUserId();
return null;
}
private CustomerArchive copyCustomer(CustomerArchive customer) {
@@ -418,7 +418,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
List<Long> deleteIdList = new ArrayList<>();
List<Long> releaseWaybillIds = new ArrayList<>();
for (LoadingManage loadingManage : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(loadingManage.getDeptId(), "配载管理");
TransportBusinessSupport.assertCurrentDept(loadingManage.getDeptId(), "配载管理", TransportBusinessSupport.MENU_LOADING_MANAGE);
if (!Objects.equals(loadingManage.getBusinessStatus(), STATUS_DRAFT)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(loadingManage.getLoadingNo());
@@ -862,7 +862,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
}
String ownLoadingNo = loadingManage.getLoadingNo();
for (Waybill waybill : waybills) {
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理");
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (ownChildrenAdmitted && LoadableAdmissionSupport.isOwnChild(waybill, ownLoadingNo)) {
continue;
}
@@ -1346,7 +1346,7 @@ public class LoadingManageServiceImpl extends BaseServiceImpl<LoadingManageMappe
private LoadingManage loadEditable(Long id, boolean checkDept) {
LoadingManage loadingManage = loadExists(id);
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(loadingManage.getDeptId(), "配载管理");
TransportBusinessSupport.assertCurrentDept(loadingManage.getDeptId(), "配载管理", TransportBusinessSupport.MENU_LOADING_MANAGE);
}
if (Objects.equals(loadingManage.getBusinessStatus(), STATUS_COMPLETED) || Objects.equals(loadingManage.getBusinessStatus(), STATUS_CANCELLED)) {
throw new ServiceException("当前状态不允许修改");
@@ -27,6 +27,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.mp.base.BaseServiceImpl;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.utils.BeanUtil;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.UserCache;
@@ -109,7 +110,7 @@ public class ProcessConfigServiceImpl extends BaseServiceImpl<ProcessConfigMappe
BusinessRemoveResultVO result = new BusinessRemoveResultVO();
List<Long> deleteIdList = new ArrayList<>();
for (ProcessConfig processConfig : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(processConfig.getDeptId(), "过程配置");
TransportBusinessSupport.assertCurrentDept(processConfig.getDeptId(), "过程配置", TransportBusinessSupport.MENU_PROCESS_CONFIG);
if (shouldSkipDelete(processConfig)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(processConfig.getConfigCode());
@@ -253,9 +254,16 @@ public class ProcessConfigServiceImpl extends BaseServiceImpl<ProcessConfigMappe
private LambdaQueryWrapper<ProcessConfig> buildQuery(ProcessConfigVO processConfig) {
LambdaQueryWrapper<ProcessConfig> queryWrapper = Wrappers.<ProcessConfig>lambdaQuery().eq(ProcessConfig::getIsDeleted, 0);
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(processConfig.getAllDept(), processConfig.getDeptId(), "过程配置");
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(processConfig.getAllDept(), processConfig.getDeptId(), "过程配置", TransportBusinessSupport.MENU_PROCESS_CONFIG);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.eq(ProcessConfig::getDeptId, deptIdList.get(0));
if (deptIdList.size() == 1) {
queryWrapper.eq(ProcessConfig::getDeptId, deptIdList.get(0));
} else {
queryWrapper.in(ProcessConfig::getDeptId, deptIdList);
}
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_PROCESS_CONFIG)) {
queryWrapper.eq(ProcessConfig::getCreateUser, AuthUtil.getUserId());
}
if (Func.isNotEmpty(processConfig.getConfigCode())) {
queryWrapper.eq(ProcessConfig::getConfigCode, processConfig.getConfigCode());
@@ -323,7 +331,7 @@ public class ProcessConfigServiceImpl extends BaseServiceImpl<ProcessConfigMappe
throw new ServiceException("过程配置不存在");
}
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(processConfig.getDeptId(), "过程配置");
TransportBusinessSupport.assertCurrentDept(processConfig.getDeptId(), "过程配置", TransportBusinessSupport.MENU_PROCESS_CONFIG);
}
return processConfig;
}
@@ -106,7 +106,7 @@ public class ShippingTemplateServiceImpl extends BaseServiceImpl<ShippingTemplat
BusinessRemoveResultVO result = new BusinessRemoveResultVO();
List<Long> deleteIdList = new ArrayList<>();
for (ShippingTemplate shippingTemplate : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(shippingTemplate.getDeptId(), "发货模板");
TransportBusinessSupport.assertCurrentDept(shippingTemplate.getDeptId(), "发货模板", TransportBusinessSupport.MENU_SHIPPING_TEMPLATE);
if (shouldSkipDelete(shippingTemplate)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(shippingTemplate.getTemplateCode());
@@ -479,7 +479,7 @@ public class ShippingTemplateServiceImpl extends BaseServiceImpl<ShippingTemplat
throw new ServiceException("发货模板不存在");
}
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(shippingTemplate.getDeptId(), "发货模板");
TransportBusinessSupport.assertCurrentDept(shippingTemplate.getDeptId(), "发货模板", TransportBusinessSupport.MENU_SHIPPING_TEMPLATE);
}
return shippingTemplate;
}
@@ -278,7 +278,7 @@ public class TemporaryCreditLimitServiceImpl extends BaseServiceImpl<TemporaryCr
if (!List.of(STATUS_DRAFT, STATUS_REJECTED, STATUS_WITHDRAWN).contains(temporaryCreditLimit.getApprovalStatus())) {
throw new ServiceException("当前状态不允许编辑");
}
TransportBusinessSupport.assertCurrentDept(temporaryCreditLimit.getApplyDeptId(), "临时额度管理");
TransportBusinessSupport.assertCurrentDept(temporaryCreditLimit.getApplyDeptId(), "临时额度管理", TransportBusinessSupport.MENU_TEMPORARY_CREDIT_LIMIT);
return temporaryCreditLimit;
}
@@ -173,7 +173,7 @@ public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMappe
BusinessRemoveResultVO result = new BusinessRemoveResultVO();
List<Long> deleteIdList = new ArrayList<>();
for (TransportPlan transportPlan : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(transportPlan.getDeptId(), "运输计划");
TransportBusinessSupport.assertCurrentDept(transportPlan.getDeptId(), "运输计划", TransportBusinessSupport.MENU_TRANSPORT_PLAN);
if (shouldSkipDelete(transportPlan)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(transportPlan.getPlanNo());
@@ -847,7 +847,7 @@ public class TransportPlanServiceImpl extends BaseServiceImpl<TransportPlanMappe
throw new ServiceException("运输计划不存在");
}
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(transportPlan.getDeptId(), "运输计划");
TransportBusinessSupport.assertCurrentDept(transportPlan.getDeptId(), "运输计划", TransportBusinessSupport.MENU_TRANSPORT_PLAN);
}
return transportPlan;
}
@@ -1302,7 +1302,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
BusinessRemoveResultVO result = new BusinessRemoveResultVO();
List<Long> deleteIdList = new ArrayList<>();
for (Waybill waybill : listByIds(idList)) {
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理");
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (shouldSkipDelete(waybill)) {
result.setSkippedCount(result.getSkippedCount() + 1);
result.getSkippedCodes().add(waybill.getWaybillNo());
@@ -1324,9 +1324,16 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
if (Func.isNotEmpty(idList)) {
queryWrapper = Wrappers.<Waybill>lambdaQuery().eq(Waybill::getIsDeleted, 0);
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(
waybill.getAllDept(), waybill.getDeptId(), "运单管理");
waybill.getAllDept(), waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.eq(Waybill::getDeptId, deptIdList.get(0));
if (deptIdList.size() == 1) {
queryWrapper.eq(Waybill::getDeptId, deptIdList.get(0));
} else {
queryWrapper.in(Waybill::getDeptId, deptIdList);
}
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_WAYBILL_MANAGE)) {
queryWrapper.eq(Waybill::getCreateUser, AuthUtil.getUserId());
}
queryWrapper.in(Waybill::getId, idList).orderByDesc(Waybill::getCreateTime);
} else {
@@ -1788,9 +1795,16 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
LambdaQueryWrapper<Waybill> queryWrapper = Wrappers.<Waybill>lambdaQuery().eq(Waybill::getIsDeleted, 0);
// 与常用线路等模块一致:超管默认不按组织过滤;普通用户 allDept!=1 时强制当前组织
List<Long> deptIdList = TransportBusinessSupport.resolveDeptFilterIds(
waybill.getAllDept(), waybill.getDeptId(), "运单管理");
waybill.getAllDept(), waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
if (Func.isNotEmpty(deptIdList)) {
queryWrapper.eq(Waybill::getDeptId, deptIdList.get(0));
if (deptIdList.size() == 1) {
queryWrapper.eq(Waybill::getDeptId, deptIdList.get(0));
} else {
queryWrapper.in(Waybill::getDeptId, deptIdList);
}
}
if (TransportBusinessSupport.isOwnCreateScope(TransportBusinessSupport.MENU_WAYBILL_MANAGE)) {
queryWrapper.eq(Waybill::getCreateUser, AuthUtil.getUserId());
}
if (Func.isNotEmpty(waybill.getWaybillNo())) {
queryWrapper.eq(Waybill::getWaybillNo, waybill.getWaybillNo());
@@ -2210,7 +2224,7 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
throw new ServiceException("运单管理不存在");
}
if (checkDept) {
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理");
TransportBusinessSupport.assertCurrentDept(waybill.getDeptId(), "运单管理", TransportBusinessSupport.MENU_WAYBILL_MANAGE);
}
return waybill;
}
@@ -27,10 +27,10 @@ import org.springblade.core.log.exception.ServiceException;
import org.springblade.core.secure.utils.AuthUtil;
import org.springblade.core.tool.constant.RoleConstant;
import org.springblade.core.tool.utils.Func;
import org.springblade.system.cache.RoleDataScopeCache;
import org.springblade.system.cache.SysCache;
import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.system.pojo.entity.User;
import org.springblade.system.pojo.vo.RoleDataScopeVO;
import java.math.BigDecimal;
import java.time.LocalDate;
@@ -52,13 +52,30 @@ public final class TransportBusinessSupport {
public static final String DATA_SOURCE_EXTERNAL = "外部系统";
private static final Pattern PHONE_PATTERN = Pattern.compile("^(1\\d{10}|0\\d{2,3}-?\\d{7,8})$");
/** 用户档案「数据权限范围」的取值:全部组织,与组织树过滤口径一致 */
private static final int DATA_SCOPE_ALL = 2;
private static final BigDecimal MIN_LONGITUDE = new BigDecimal("-180");
private static final BigDecimal MAX_LONGITUDE = new BigDecimal("180");
private static final BigDecimal MIN_LATITUDE = new BigDecimal("-90");
private static final BigDecimal MAX_LATITUDE = new BigDecimal("90");
/** 运单管理菜单编码 */
public static final String MENU_WAYBILL_MANAGE = "waybill_manage";
/** 配载管理菜单编码 */
public static final String MENU_LOADING_MANAGE = "loading_manage";
/** 运输计划菜单编码 */
public static final String MENU_TRANSPORT_PLAN = "transport_plan";
/** 常用线路菜单编码 */
public static final String MENU_COMMON_ROUTE = "common_route";
/** 常用货物菜单编码 */
public static final String MENU_COMMON_CARGO = "common_cargo";
/** 常用地址菜单编码 */
public static final String MENU_COMMON_ADDRESS = "common_address";
/** 过程配置菜单编码 */
public static final String MENU_PROCESS_CONFIG = "process_config";
/** 发货模板菜单编码 */
public static final String MENU_SHIPPING_TEMPLATE = "shipping_template";
/** 临时额度管理菜单编码 */
public static final String MENU_TEMPORARY_CREDIT_LIMIT = "temporary_credit_limit";
private TransportBusinessSupport() {
}
@@ -81,23 +98,43 @@ public final class TransportBusinessSupport {
}
/**
* 是否被授予跨组织数据权限:超级管理员,或用户档案「数据权限范围」为全部组织。
* <p>
* 「数据权限范围」在用户管理里配置,组织树已按它过滤(DeptServiceImpl 的组织树数据权限);
* 运输业务的列表过滤与写入校验必须与组织树同一口径。
* 解析指定菜单下的角色数据权限(超管视为全部组织)。
*/
public static RoleDataScopeVO resolveRoleDataScope(String menuCode) {
if (isAdministrator()) {
return RoleDataScopeVO.all();
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveByMenuCode(menuCode);
return scope == null ? RoleDataScopeVO.all() : scope;
}
/**
* 是否被授予跨组织数据权限:超级管理员,或角色在该菜单下配置为全部组织。
*
* @param menuCode 菜单编码
* @return true-不限组织
*/
public static boolean hasAllDeptScope(String menuCode) {
return resolveRoleDataScope(menuCode).isAllDept();
}
/**
* 兼容旧调用:无菜单上下文时按全部角色菜单配置最宽合并。
*/
public static boolean hasAllDeptScope() {
if (isAdministrator()) {
return true;
}
Long userId = AuthUtil.getUserId();
if (Func.isEmpty(userId) || userId <= 0) {
return false;
}
User user = UserCache.getUser(userId);
return Objects.equals(user == null ? null : user.getDataScopeRange(), DATA_SCOPE_ALL);
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
return scope == null || scope.isAllDept();
}
/**
* 是否仅本人创建数据权限
*/
public static boolean isOwnCreateScope(String menuCode) {
RoleDataScopeVO scope = resolveRoleDataScope(menuCode);
return scope.isOwnCreate();
}
public static Long currentDeptId(String moduleName) {
@@ -118,18 +155,52 @@ public final class TransportBusinessSupport {
/**
* 校验目标业务数据属于当前用户可操作的组织。
* <p>
* 超管与「数据权限范围=全部组织」的用户不限组织;其余用户仍严格要求与当前会话组织相等。
*/
public static void assertCurrentDept(Long deptId, String moduleName, String menuCode) {
RoleDataScopeVO scope = resolveRoleDataScope(menuCode);
if (scope.isAllDept() || scope.isOwnCreate()) {
return;
}
List<Long> allowedDeptIds = scope.getDeptIds();
if (Func.isEmpty(allowedDeptIds)) {
if (!Objects.equals(deptId, currentDeptId(moduleName))) {
throw new ServiceException("无权操作其他组织" + moduleName);
}
return;
}
if (!allowedDeptIds.contains(deptId)) {
throw new ServiceException("无权操作其他组织" + moduleName);
}
}
/**
* 兼容旧调用:按最宽合并校验
*/
public static void assertCurrentDept(Long deptId, String moduleName) {
if (hasAllDeptScope()) {
return;
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
if (scope != null && scope.isOwnCreate()) {
return;
}
if (scope != null && Func.isNotEmpty(scope.getDeptIds())) {
if (!scope.getDeptIds().contains(deptId)) {
throw new ServiceException("无权操作其他组织" + moduleName);
}
return;
}
if (!Objects.equals(deptId, currentDeptId(moduleName))) {
throw new ServiceException("无权操作其他组织" + moduleName);
}
}
public static void validateAllDept(Integer allDept, String moduleName, String menuCode) {
if (Objects.equals(allDept, 1) && !hasAllDeptScope(menuCode)) {
throw new ServiceException("无权查看全部组织" + moduleName);
}
}
public static void validateAllDept(Integer allDept, String moduleName) {
if (Objects.equals(allDept, 1) && !hasAllDeptScope()) {
throw new ServiceException("无权查看全部组织" + moduleName);
@@ -138,15 +209,58 @@ public final class TransportBusinessSupport {
/**
* 解析列表组织筛选 ID。
* <p>
* 普通用户 allDept!=1 时强制当前组织;超管与「全部组织」用户默认不按组织过滤,
* 仅在 allDept=1 且传入 deptId 时精确匹配该组织。
*
* @param allDept 是否查看全部组织
* @param deptId 筛选组织 ID
* @param moduleName 模块名称
* @param menuCode 菜单编码
* @return 需要过滤的组织 ID 集合;空集合表示不过滤组织
*/
public static List<Long> resolveDeptFilterIds(Integer allDept, Long deptId, String moduleName, String menuCode) {
RoleDataScopeVO scope = resolveRoleDataScope(menuCode);
if (scope.isAllDept()) {
validateAllDept(allDept, moduleName, menuCode);
if (Objects.equals(allDept, 1) && Func.isNotEmpty(deptId) && deptId > 0) {
return Collections.singletonList(deptId);
}
return Collections.emptyList();
}
if (scope.isOwnCreate()) {
// 本人创建不按组织收口;若前端传了组织筛选则精确匹配
if (Objects.equals(allDept, 1) && Func.isNotEmpty(deptId) && deptId > 0) {
return Collections.singletonList(deptId);
}
return Collections.emptyList();
}
List<Long> allowedDeptIds = scope.getDeptIds();
if (Func.isEmpty(allowedDeptIds)) {
if (!Objects.equals(allDept, 1)) {
return Collections.singletonList(currentDeptId(moduleName));
}
if (Func.isEmpty(deptId) || deptId <= 0) {
return Collections.emptyList();
}
return Collections.singletonList(deptId);
}
if (Objects.equals(allDept, 1)) {
if (Func.isEmpty(deptId) || deptId <= 0) {
return allowedDeptIds;
}
if (!allowedDeptIds.contains(deptId)) {
throw new ServiceException("无权查看该组织" + moduleName);
}
return Collections.singletonList(deptId);
}
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
if (Func.isNotEmpty(currentDeptId) && allowedDeptIds.contains(currentDeptId)) {
return Collections.singletonList(currentDeptId);
}
return allowedDeptIds;
}
/**
* 兼容旧调用:按最宽合并解析组织筛选
*/
public static List<Long> resolveDeptFilterIds(Integer allDept, Long deptId, String moduleName) {
validateAllDept(allDept, moduleName);
if (hasAllDeptScope()) {
@@ -155,14 +269,35 @@ public final class TransportBusinessSupport {
}
return Collections.emptyList();
}
RoleDataScopeVO scope = RoleDataScopeCache.resolveWidest();
if (scope != null && scope.isOwnCreate()) {
if (Objects.equals(allDept, 1) && Func.isNotEmpty(deptId) && deptId > 0) {
return Collections.singletonList(deptId);
}
return Collections.emptyList();
}
if (scope != null && Func.isNotEmpty(scope.getDeptIds())) {
if (Objects.equals(allDept, 1)) {
if (Func.isEmpty(deptId) || deptId <= 0) {
return scope.getDeptIds();
}
if (!scope.getDeptIds().contains(deptId)) {
throw new ServiceException("无权查看该组织" + moduleName);
}
return Collections.singletonList(deptId);
}
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
if (Func.isNotEmpty(currentDeptId) && scope.getDeptIds().contains(currentDeptId)) {
return Collections.singletonList(currentDeptId);
}
return scope.getDeptIds();
}
if (!Objects.equals(allDept, 1)) {
return Collections.singletonList(currentDeptId(moduleName));
}
// Jackson 空值可能被反序列化为 -1,这里与空值同等对待,避免误过滤。
if (Func.isEmpty(deptId) || deptId <= 0) {
return Collections.emptyList();
}
// 列表组织筛选项为单选,按所选组织精确过滤,不自动展开下级。
return Collections.singletonList(deptId);
}