✨ 组织树独立权限、平台公司回写、合同计费客商角色与查看菜单补齐

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-11-04 03:15:42 +08:00
parent a0e30d5834
commit 69f9c9af7f
26 changed files with 745 additions and 98 deletions
@@ -310,7 +310,7 @@ public class CommonAddressServiceImpl extends BaseServiceImpl<CommonAddressMappe
private void fillDisplay(CommonAddressVO address) {
address.setSiteCodeDisplay(TYPE_NORMAL.equals(address.getAddressType()) ? NORMAL_SITE_CODE : address.getSiteCode());
address.setReadonly(!TransportBusinessSupport.isAdministrator() && !Objects.equals(address.getDeptId(), currentDeptId()));
address.setReadonly(TransportBusinessSupport.isDeptReadonly(address.getDeptId(), TransportBusinessSupport.MENU_COMMON_ADDRESS));
address.setCreateUserName(UserCache.getUserRealName(address.getCreateUser()));
address.setUpdateUserName(UserCache.getUserRealName(address.getUpdateUser()));
}
@@ -60,7 +60,6 @@ import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
@@ -646,6 +645,7 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
.eq(Func.isNotEmpty(contractManage.getArchiveStatus()), ContractManage::getArchiveStatus, contractManage.getArchiveStatus())
.eq(Func.isNotEmpty(contractManage.getContractStage()), ContractManage::getContractStage, contractManage.getContractStage())
.orderByDesc(ContractManage::getCreateTime);
applyPartyName(queryWrapper, contractManage.getPartyName());
applyEffectiveType(queryWrapper, contractManage.getEffectiveType());
applyExpireScope(queryWrapper, contractManage.getExpireScope());
// 是否同步金蝶(kingdee_id 是否已回填)/ 是否同步合规(hcs_sync_status 是否 synced)
@@ -671,6 +671,18 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
}
}
/**
* 客商筛选:匹配合同甲/乙/丙方名称(模糊)
*/
private void applyPartyName(LambdaQueryWrapper<ContractManage> queryWrapper, String partyName) {
if (Func.isEmpty(partyName)) {
return;
}
queryWrapper.and(wrapper -> wrapper.like(ContractManage::getPartyA, partyName)
.or().like(ContractManage::getPartyB, partyName)
.or().like(ContractManage::getPartyC, partyName));
}
private void applyEffectiveType(LambdaQueryWrapper<ContractManage> queryWrapper, String effectiveType) {
if (Func.isEmpty(effectiveType)) {
return;
@@ -700,7 +712,7 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
.lt(ContractManage::getTemporaryEndDate, today)
.or(wrapper2 -> wrapper2.ne(ContractManage::getContractStage, STAGE_TEMPORARY)
.lt(ContractManage::getEndDate, today)));
default -> throw new ServiceException("无效的生效类型筛选条件");
default -> throw new ServiceException("无效的合同状态筛选条件");
}
}
@@ -1049,18 +1061,11 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
if (!(parsed instanceof List<?> plans)) {
throw new ServiceException("计费方案设置格式不正确");
}
Map<String, Integer> defaultPlanCount = new HashMap<>();
for (Object value : plans) {
if (!(value instanceof Map<?, ?> plan)) {
throw new ServiceException("计费方案设置格式不正确");
}
String transportMode = trimValue(plan.get("transportMode"));
if (isDefaultBillingPlan(plan)) {
int count = defaultPlanCount.merge(transportMode, 1, Integer::sum);
if (count > 1) {
throw new ServiceException("同一运输方式仅支持配置一个默认计费方案");
}
}
// 一个客商对应一个方案,不再校验「默认方案」
validateBillingMatchConditions(plan);
validateBillingRuleTaxRates(plan);
}
@@ -1122,15 +1127,6 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
return value != null && !String.valueOf(value).trim().isEmpty();
}
private boolean isDefaultBillingPlan(Map<?, ?> plan) {
Object value = plan.get("defaultPlan");
return Boolean.TRUE.equals(value) || "true".equalsIgnoreCase(String.valueOf(value)) || "1".equals(String.valueOf(value));
}
private String trimValue(Object value) {
return value == null ? null : TransportBusinessSupport.trimToNull(String.valueOf(value));
}
private void validatePaymentRatios(String paymentRatioJson) {
if (Func.isEmpty(paymentRatioJson)) return;
try {
@@ -93,7 +93,8 @@ public final class ContractPartySupport {
}
/**
* 校验计费方案均已绑定合同明细中的客商
* 校验计费方案均已绑定合同明细中的客商。
* 客户合同仅允许绑定甲方;承运商合同仅允许绑定乙方/丙方。
*/
public static void validateBillingPlanBindings(ContractManage contractManage) {
String billingPlanJson = contractManage.getBillingPlanJson();
@@ -104,7 +105,10 @@ public final class ContractPartySupport {
if (!(parsed instanceof List<?> plans) || plans.isEmpty()) {
return;
}
Map<String, Map<String, Object>> partyByRowId = indexPartyRows(contractManage);
Set<String> allowedRoles = allowedBillingPartyRoles(contractManage.getContractCategory());
Map<String, Map<String, Object>> partyByRowId = indexPartyRows(contractManage).entrySet().stream()
.filter(entry -> allowedRoles.contains(normalizeRole(String.valueOf(entry.getValue().get("partyRole")))))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (left, right) -> left, LinkedHashMap::new));
Set<String> customerKeys = partyByRowId.values().stream()
.map(ContractPartySupport::customerMatchKey)
.filter(Func::isNotEmpty)
@@ -121,6 +125,9 @@ public final class ContractPartySupport {
if (Func.isEmpty(partyRowId) && Func.isEmpty(customerId) && Func.isEmpty(customerName)) {
throw new ServiceException("计费方案第" + (index + 1) + "个必须选择适用客商");
}
if (Func.isNotEmpty(partyRole) && !allowedRoles.contains(partyRole)) {
throw new ServiceException(billingPartyRoleMessage(contractManage.getContractCategory(), index + 1));
}
boolean matched = false;
if (Func.isNotEmpty(partyRowId) && partyByRowId.containsKey(partyRowId)) {
matched = true;
@@ -134,11 +141,36 @@ public final class ContractPartySupport {
}
}
if (!matched) {
throw new ServiceException("计费方案第" + (index + 1) + "个适用客商不在合同甲/乙/丙明细中");
throw new ServiceException(billingPartyRoleMessage(contractManage.getContractCategory(), index + 1));
}
}
}
/**
* 计费方案可选客商角色:客户合同=甲方;承运商合同=乙方/丙方
*/
public static Set<String> allowedBillingPartyRoles(String contractCategory) {
String category = Func.toStr(contractCategory).trim();
if ("承运商合同".equals(category)) {
return Set.of(ROLE_B, ROLE_C);
}
if ("客户合同".equals(category)) {
return Set.of(ROLE_A);
}
return Set.of(ROLE_A, ROLE_B, ROLE_C);
}
private static String billingPartyRoleMessage(String contractCategory, int planIndex) {
String category = Func.toStr(contractCategory).trim();
if ("承运商合同".equals(category)) {
return "计费方案第" + planIndex + "个适用客商只能选择乙方或丙方";
}
if ("客户合同".equals(category)) {
return "计费方案第" + planIndex + "个适用客商只能选择甲方";
}
return "计费方案第" + planIndex + "个适用客商不在合同甲/乙/丙明细中";
}
public static List<Map<String, Object>> parsePartyRows(String partyJson) {
if (Func.isEmpty(partyJson)) {
return List.of();
@@ -23,12 +23,11 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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;
import org.springblade.transport.pojo.entity.CommonCargo;
import org.springblade.transport.pojo.vo.CommonCargoVO;
import org.springblade.transport.support.TransportBusinessSupport;
import java.math.BigDecimal;
import java.util.Objects;
@@ -52,8 +51,7 @@ public class CommonCargoWrapper extends BaseEntityWrapper<CommonCargo, CommonCar
}
commonCargoVO.setCreateUserName(UserCache.getUserRealName(commonCargo.getCreateUser()));
commonCargoVO.setUpdateUserName(UserCache.getUserRealName(commonCargo.getUpdateUser()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
commonCargoVO.setReadonly(currentDeptId != null && !Objects.equals(commonCargo.getDeptId(), currentDeptId));
commonCargoVO.setReadonly(TransportBusinessSupport.isDeptReadonly(commonCargo.getDeptId(), TransportBusinessSupport.MENU_COMMON_CARGO));
return commonCargoVO;
}
@@ -23,12 +23,11 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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;
import org.springblade.transport.pojo.entity.CommonRoute;
import org.springblade.transport.pojo.vo.CommonRouteVO;
import org.springblade.transport.support.TransportBusinessSupport;
import java.util.Objects;
@@ -48,8 +47,7 @@ public class CommonRouteWrapper extends BaseEntityWrapper<CommonRoute, CommonRou
CommonRouteVO commonRouteVO = Objects.requireNonNull(BeanUtil.copyProperties(commonRoute, CommonRouteVO.class));
commonRouteVO.setCreateUserName(UserCache.getUserRealName(commonRoute.getCreateUser()));
commonRouteVO.setUpdateUserName(UserCache.getUserRealName(commonRoute.getUpdateUser()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
commonRouteVO.setReadonly(currentDeptId != null && !Objects.equals(commonRoute.getDeptId(), currentDeptId));
commonRouteVO.setReadonly(TransportBusinessSupport.isDeptReadonly(commonRoute.getDeptId(), TransportBusinessSupport.MENU_COMMON_ROUTE));
return commonRouteVO;
}
@@ -5,12 +5,11 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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;
import org.springblade.transport.pojo.entity.LoadingManage;
import org.springblade.transport.pojo.vo.LoadingManageVO;
import org.springblade.transport.support.TransportBusinessSupport;
import java.math.BigDecimal;
import java.util.Objects;
@@ -31,8 +30,7 @@ public class LoadingManageWrapper extends BaseEntityWrapper<LoadingManage, Loadi
LoadingManageVO loadingManageVO = Objects.requireNonNull(BeanUtil.copyProperties(loadingManage, LoadingManageVO.class));
loadingManageVO.setCreateUserName(UserCache.getUserRealName(loadingManage.getCreateUser()));
loadingManageVO.setUpdateUserName(UserCache.getUserRealName(loadingManage.getUpdateUser()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
loadingManageVO.setReadonly(currentDeptId != null && !Objects.equals(loadingManage.getDeptId(), currentDeptId));
loadingManageVO.setReadonly(TransportBusinessSupport.isDeptReadonly(loadingManage.getDeptId(), TransportBusinessSupport.MENU_LOADING_MANAGE));
loadingManageVO.setBusinessStatusName(businessStatusName(loadingManage.getBusinessStatus()));
// 未维护里程的占位值 -1 对外展示为空
if (loadingManageVO.getMileage() != null
@@ -23,12 +23,11 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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;
import org.springblade.transport.pojo.entity.ProcessConfig;
import org.springblade.transport.pojo.vo.ProcessConfigVO;
import org.springblade.transport.support.TransportBusinessSupport;
import java.util.Objects;
@@ -48,8 +47,7 @@ public class ProcessConfigWrapper extends BaseEntityWrapper<ProcessConfig, Proce
ProcessConfigVO processConfigVO = Objects.requireNonNull(BeanUtil.copyProperties(processConfig, ProcessConfigVO.class));
processConfigVO.setCreateUserName(UserCache.getUserRealName(processConfig.getCreateUser()));
processConfigVO.setUpdateUserName(UserCache.getUserRealName(processConfig.getUpdateUser()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
processConfigVO.setReadonly(currentDeptId != null && !Objects.equals(processConfig.getDeptId(), currentDeptId));
processConfigVO.setReadonly(TransportBusinessSupport.isDeptReadonly(processConfig.getDeptId(), TransportBusinessSupport.MENU_PROCESS_CONFIG));
processConfigVO.setStatusName(statusName(processConfig.getStatus()));
return processConfigVO;
}
@@ -23,12 +23,11 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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;
import org.springblade.transport.pojo.entity.ShippingTemplate;
import org.springblade.transport.pojo.vo.ShippingTemplateVO;
import org.springblade.transport.support.TransportBusinessSupport;
import java.util.Objects;
@@ -48,8 +47,7 @@ public class ShippingTemplateWrapper extends BaseEntityWrapper<ShippingTemplate,
ShippingTemplateVO shippingTemplateVO = Objects.requireNonNull(BeanUtil.copyProperties(shippingTemplate, ShippingTemplateVO.class));
shippingTemplateVO.setCreateUserName(UserCache.getUserRealName(shippingTemplate.getCreateUser()));
shippingTemplateVO.setUpdateUserName(UserCache.getUserRealName(shippingTemplate.getUpdateUser()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
shippingTemplateVO.setReadonly(currentDeptId != null && !Objects.equals(shippingTemplate.getDeptId(), currentDeptId));
shippingTemplateVO.setReadonly(TransportBusinessSupport.isDeptReadonly(shippingTemplate.getDeptId(), TransportBusinessSupport.MENU_SHIPPING_TEMPLATE));
return shippingTemplateVO;
}
@@ -23,9 +23,7 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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;
import org.springblade.transport.pojo.entity.TransportPlan;
import org.springblade.transport.pojo.vo.TransportPlanVO;
@@ -50,8 +48,7 @@ public class TransportPlanWrapper extends BaseEntityWrapper<TransportPlan, Trans
transportPlanVO.setCreateUserName(UserCache.getUserRealName(transportPlan.getCreateUser()));
transportPlanVO.setUpdateUserName(UserCache.getUserRealName(transportPlan.getUpdateUser()));
transportPlanVO.setDataSource(TransportBusinessSupport.normalizeTransportPlanDataSource(transportPlan.getDataSource()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
transportPlanVO.setReadonly(currentDeptId != null && !Objects.equals(transportPlan.getDeptId(), currentDeptId));
transportPlanVO.setReadonly(TransportBusinessSupport.isDeptReadonly(transportPlan.getDeptId(), TransportBusinessSupport.MENU_TRANSPORT_PLAN));
transportPlanVO.setBusinessStatusName(businessStatusName(transportPlan.getBusinessStatus()));
return transportPlanVO;
}
@@ -23,7 +23,6 @@
package org.springblade.transport.wrapper;
import org.springblade.core.mp.support.BaseEntityWrapper;
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.DictBizCache;
@@ -54,8 +53,7 @@ public class WaybillWrapper extends BaseEntityWrapper<Waybill, WaybillVO> {
waybillVO.setCreateUserName(UserCache.getUserRealName(waybill.getCreateUser()));
waybillVO.setUpdateUserName(UserCache.getUserRealName(waybill.getUpdateUser()));
waybillVO.setDataSource(TransportBusinessSupport.normalizeWaybillDataSource(waybill.getDataSource()));
Long currentDeptId = Func.firstLong(AuthUtil.getDeptId());
waybillVO.setReadonly(currentDeptId != null && !Objects.equals(waybill.getDeptId(), currentDeptId));
waybillVO.setReadonly(TransportBusinessSupport.isDeptReadonly(waybill.getDeptId(), TransportBusinessSupport.MENU_WAYBILL_MANAGE));
String displayStatus = WaybillProcessSupport.normalizeBusinessStatus(
waybill.getBusinessStatus(), waybill.getProcessJson(), waybill.getDriverAcceptStatus());
waybillVO.setBusinessStatus(displayStatus);