♻️ 生效配置与运单快照的取法、二选一规则收进 LiveProcessConfigSupport 一处
原先三处各写一份(运单服务、司机运单服务、运次读服务),且规则并不完全一样:司机端与运次读服务 在生效配置没有可打卡节点时回退快照,运单服务不回退。两份长得一样却不一样,改一处没有任何提示。 新类把取配置(单项目 LIKE + 逗号分段精确比对、批量一次查全)与两个变体都收在一处,并在 javadoc 里 写明各自适用面:接单需求、节点门禁、节点订正读 resolve(生效配置是权威,不能被快照顶掉); 司机端打卡页与列表卡读 resolveForPunch(配置被改坏时回退快照,避免打卡页空白)。 三处的 SQL 形状与逐分支行为保持不变,调用点只留一行委托。 新增 LiveProcessConfigSupportTest(8 项)覆盖两条规则的全部形态与项目 id 精确比对; blade-transport 全量 365 项用例通过。
This commit is contained in:
+6
-45
@@ -48,8 +48,8 @@ import org.springblade.transport.pojo.entity.ExceptionDisposal;
|
||||
import org.springblade.transport.pojo.entity.TransportVehicle;
|
||||
import org.springblade.transport.service.ITransportVehicleService;
|
||||
import org.springblade.transport.support.CertificationGate;
|
||||
import org.springblade.transport.support.LiveProcessConfigSupport;
|
||||
import org.springblade.transport.support.VehiclePlateSupport;
|
||||
import org.springblade.transport.pojo.entity.ProcessConfig;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.entity.WaybillEnroutePunch;
|
||||
import org.springblade.transport.pojo.entity.WaybillNodePunch;
|
||||
@@ -1185,55 +1185,16 @@ public class DriverWaybillServiceImpl implements IDriverWaybillService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 优先用项目启用中的过程配置;若动态配置无打卡节点,回退运单快照 processJson,
|
||||
* 避免项目配置改坏后司机端打卡页空白。
|
||||
* 优先用项目启用中的过程配置;若动态配置无打卡节点,回退运单快照,
|
||||
* 避免项目配置改坏后司机端打卡页空白。取法与规则都在 {@link LiveProcessConfigSupport}。
|
||||
*/
|
||||
private String resolveProcessJson(Waybill waybill) {
|
||||
if (waybill == null) {
|
||||
return null;
|
||||
}
|
||||
String snapshot = waybill.getProcessJson();
|
||||
String live = loadLiveProcessConfigJson(waybill.getProjectId());
|
||||
if (Func.isNotEmpty(live)) {
|
||||
if (!WaybillProcessSupport.listDriverPunchNodes(live).isEmpty()) {
|
||||
return live;
|
||||
}
|
||||
// 动态配置存在但无可打卡节点:仍回退快照
|
||||
if (Func.isNotEmpty(snapshot)
|
||||
&& !WaybillProcessSupport.listDriverPunchNodes(snapshot).isEmpty()) {
|
||||
return snapshot;
|
||||
}
|
||||
return live;
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private String loadLiveProcessConfigJson(Long projectId) {
|
||||
if (projectId == null) {
|
||||
return null;
|
||||
}
|
||||
String projectIdStr = String.valueOf(projectId);
|
||||
return processConfigService.list(Wrappers.<ProcessConfig>lambdaQuery()
|
||||
.eq(ProcessConfig::getStatus, 1)
|
||||
.eq(ProcessConfig::getIsDeleted, 0)
|
||||
.like(ProcessConfig::getProjectIds, projectIdStr)
|
||||
.orderByDesc(ProcessConfig::getUpdateTime)
|
||||
.orderByDesc(ProcessConfig::getCreateTime))
|
||||
.stream()
|
||||
.filter(cfg -> containsProjectId(cfg.getProjectIds(), projectIdStr))
|
||||
.map(ProcessConfig::getNodeConfigJson)
|
||||
.filter(Func::isNotEmpty)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private boolean containsProjectId(String projectIds, String projectId) {
|
||||
if (Func.isEmpty(projectIds) || Func.isEmpty(projectId)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.stream(projectIds.split(","))
|
||||
.map(String::trim)
|
||||
.anyMatch(projectId::equals);
|
||||
return LiveProcessConfigSupport.resolveForPunch(
|
||||
LiveProcessConfigSupport.byProject(processConfigService, waybill.getProjectId()),
|
||||
waybill.getProcessJson());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-46
@@ -30,7 +30,6 @@ import lombok.RequiredArgsConstructor;
|
||||
import org.springblade.core.log.exception.ServiceException;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.transport.pojo.entity.LoadingManage;
|
||||
import org.springblade.transport.pojo.entity.ProcessConfig;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.vo.TripQueryVO;
|
||||
import org.springblade.transport.pojo.vo.TripStationVO;
|
||||
@@ -40,12 +39,12 @@ import org.springblade.transport.service.ILoadingManageService;
|
||||
import org.springblade.transport.service.IProcessConfigService;
|
||||
import org.springblade.transport.service.ITripQueryService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.LiveProcessConfigSupport;
|
||||
import org.springblade.transport.support.WaybillProcessSupport;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.LinkedHashMap;
|
||||
@@ -287,6 +286,7 @@ public class TripQueryServiceImpl implements ITripQueryService {
|
||||
* 按单票路径的口径把生效中的过程配置写回内存里的行:项目启用中的配置优先,它没有可打卡节点时回退运单快照。
|
||||
* <p>
|
||||
* 只改内存里的对象,分档与卡片下发都读它,不落库;生效配置一次取回后按项目复用,不逐条回查。
|
||||
* 取法与二选一规则都在 {@link LiveProcessConfigSupport},这里只做批量写回。
|
||||
*/
|
||||
private void applyLiveProcessConfig(List<Waybill> rows) {
|
||||
if (Func.isEmpty(rows)) {
|
||||
@@ -297,60 +297,18 @@ public class TripQueryServiceImpl implements ITripQueryService {
|
||||
if (projectIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<Long, String> live = liveProcessConfigs(projectIds);
|
||||
Map<Long, String> live = LiveProcessConfigSupport.byProjects(processConfigService, projectIds);
|
||||
if (live.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Waybill row : rows) {
|
||||
String liveJson = live.get(row.getProjectId());
|
||||
if (Func.isNotEmpty(liveJson)) {
|
||||
row.setProcessJson(resolveProcessJson(liveJson, row.getProcessJson()));
|
||||
row.setProcessJson(LiveProcessConfigSupport.resolveForPunch(liveJson, row.getProcessJson()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 生效配置与快照二选一:生效配置没有可打卡节点时回退快照,两边都取不到节点就按生效配置来 */
|
||||
private String resolveProcessJson(String liveJson, String snapshot) {
|
||||
if (WaybillProcessSupport.listDriverPunchNodes(liveJson).isEmpty()
|
||||
&& Func.isNotEmpty(snapshot)
|
||||
&& !WaybillProcessSupport.listDriverPunchNodes(snapshot).isEmpty()) {
|
||||
return snapshot;
|
||||
}
|
||||
return liveJson;
|
||||
}
|
||||
|
||||
/** 生效中的过程配置:项目 → 节点配置 JSON,取最近更新的一条 */
|
||||
private Map<Long, String> liveProcessConfigs(Set<Long> projectIds) {
|
||||
List<ProcessConfig> configs = processConfigService.list(Wrappers.<ProcessConfig>lambdaQuery()
|
||||
.eq(ProcessConfig::getStatus, 1)
|
||||
.eq(ProcessConfig::getIsDeleted, 0)
|
||||
.orderByDesc(ProcessConfig::getUpdateTime)
|
||||
.orderByDesc(ProcessConfig::getCreateTime));
|
||||
Map<Long, String> live = new LinkedHashMap<>();
|
||||
for (Long projectId : projectIds) {
|
||||
if (projectId == null) {
|
||||
continue;
|
||||
}
|
||||
String projectIdStr = String.valueOf(projectId);
|
||||
for (ProcessConfig config : configs) {
|
||||
if (Func.isEmpty(config.getNodeConfigJson())
|
||||
|| !containsProjectId(config.getProjectIds(), projectIdStr)) {
|
||||
continue;
|
||||
}
|
||||
live.put(projectId, config.getNodeConfigJson());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return live;
|
||||
}
|
||||
|
||||
private boolean containsProjectId(String projectIds, String projectId) {
|
||||
if (Func.isEmpty(projectIds) || Func.isEmpty(projectId)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.stream(projectIds.split(",")).map(String::trim).anyMatch(projectId::equals);
|
||||
}
|
||||
|
||||
/**
|
||||
* 有子单被拒、且这趟车还没结束的运次键:整单拒绝后这趟车不再算待处理
|
||||
*/
|
||||
|
||||
+13
-29
@@ -45,7 +45,6 @@ import org.springblade.transport.mapper.WaybillMapper;
|
||||
import org.springblade.transport.mapper.WaybillNodePunchMapper;
|
||||
import org.springblade.transport.pojo.entity.LoadingManage;
|
||||
import org.springblade.transport.pojo.entity.ContractManage;
|
||||
import org.springblade.transport.pojo.entity.ProcessConfig;
|
||||
import org.springblade.transport.pojo.entity.ProjectApply;
|
||||
import org.springblade.transport.pojo.entity.Waybill;
|
||||
import org.springblade.transport.pojo.entity.WaybillEnroutePunch;
|
||||
@@ -66,6 +65,7 @@ import org.springblade.transport.service.IProjectApplyService;
|
||||
import org.springblade.transport.service.IReceivablePayableDetailService;
|
||||
import org.springblade.transport.service.IContractManageService;
|
||||
import org.springblade.transport.service.IWaybillService;
|
||||
import org.springblade.transport.support.LiveProcessConfigSupport;
|
||||
import org.springblade.transport.support.TransportBusinessSupport;
|
||||
import org.springblade.transport.support.WaybillProcessSupport;
|
||||
import org.springblade.transport.wrapper.WaybillWrapper;
|
||||
@@ -822,27 +822,20 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 动态过程配置优先,回退运单快照 */
|
||||
/**
|
||||
* 生效配置优先,回退运单快照。
|
||||
* <p>
|
||||
* 取法与二选一规则在 {@link LiveProcessConfigSupport};这里不用「生效配置没有可打卡节点就回退快照」
|
||||
* 那一版——接单需求、节点门禁、节点订正都读这份配置判断节点存不存在,生效配置是权威,
|
||||
* 不能因为快照里有节点而被顶掉。
|
||||
*/
|
||||
private String resolveLiveProcessJson(Waybill waybill) {
|
||||
if (waybill.getProjectId() != null) {
|
||||
String projectId = String.valueOf(waybill.getProjectId());
|
||||
String live = processConfigService.list(Wrappers.<ProcessConfig>lambdaQuery()
|
||||
.eq(ProcessConfig::getStatus, 1)
|
||||
.eq(ProcessConfig::getIsDeleted, 0)
|
||||
.like(ProcessConfig::getProjectIds, projectId)
|
||||
.orderByDesc(ProcessConfig::getUpdateTime)
|
||||
.orderByDesc(ProcessConfig::getCreateTime))
|
||||
.stream()
|
||||
.filter(cfg -> containsProjectId(cfg.getProjectIds(), projectId))
|
||||
.map(ProcessConfig::getNodeConfigJson)
|
||||
.filter(Func::isNotEmpty)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
if (Func.isNotEmpty(live)) {
|
||||
return live;
|
||||
}
|
||||
if (waybill == null) {
|
||||
return null;
|
||||
}
|
||||
return waybill.getProcessJson();
|
||||
return LiveProcessConfigSupport.resolve(
|
||||
LiveProcessConfigSupport.byProject(processConfigService, waybill.getProjectId()),
|
||||
waybill.getProcessJson());
|
||||
}
|
||||
|
||||
private Map<String, List<String>> buildVoucherTypesIndex(String processJson) {
|
||||
@@ -1175,15 +1168,6 @@ public class WaybillServiceImpl extends BaseServiceImpl<WaybillMapper, Waybill>
|
||||
waybill.setDriverRejectReason(null);
|
||||
}
|
||||
|
||||
private boolean containsProjectId(String projectIds, String projectId) {
|
||||
if (Func.isEmpty(projectIds)) {
|
||||
return false;
|
||||
}
|
||||
return List.of(projectIds.split(",")).stream()
|
||||
.map(String::trim)
|
||||
.anyMatch(projectId::equals);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public BusinessRemoveResultVO removeWaybill(String ids) {
|
||||
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement
|
||||
* obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license
|
||||
* from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party
|
||||
* without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments
|
||||
* from this software for such purposes. Copyright of this software
|
||||
* remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software
|
||||
* must not be used for illegal purposes.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.support;
|
||||
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.transport.pojo.entity.ProcessConfig;
|
||||
import org.springblade.transport.service.IProcessConfigService;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 过程配置的取法与「生效配置还是运单快照」的二选一规则,只此一处。
|
||||
* <p>
|
||||
* 生效配置({@code blade_process_config})按项目生效,是活的:改一次,该项目下所有运单下次读取
|
||||
* 都按新配置呈现;运单创建时另存一份 {@code process_json} 快照,只在生效配置取不到时兜底。
|
||||
* 生效配置按更新时间倒序取第一条命中该项目的,项目集合是逗号串,故先 LIKE 粗筛再按逗号分段精确比对 ——
|
||||
* 只靠 LIKE 会把「1234」命中到「12345」上。
|
||||
* <p>
|
||||
* 两个二选一版本的区别是**得到什么结果**,不是风格:
|
||||
* <ul>
|
||||
* <li>{@link #resolve} 生效配置优先 —— 接单需求、节点门禁、节点订正这些判断节点存不存在的路径用它。
|
||||
* 生效配置是权威,即使它没配可打卡节点也不能被快照顶掉,否则判断依据会随时间漂移。</li>
|
||||
* <li>{@link #resolveForPunch} 生效配置没有可打卡节点时回退快照 —— 司机端打卡页与列表卡用它。
|
||||
* 配置被改坏(节点全删)时,司机端仍能按快照渲染出打卡节点,不至于空白。</li>
|
||||
* </ul>
|
||||
*/
|
||||
public final class LiveProcessConfigSupport {
|
||||
|
||||
private LiveProcessConfigSupport() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 单个项目的生效配置 JSON,没有则返回 null
|
||||
*
|
||||
* @param service 过程配置服务
|
||||
* @param projectId 项目主键
|
||||
*/
|
||||
public static String byProject(IProcessConfigService service, Long projectId) {
|
||||
if (service == null || projectId == null) {
|
||||
return null;
|
||||
}
|
||||
String projectIdStr = String.valueOf(projectId);
|
||||
return service.list(Wrappers.<ProcessConfig>lambdaQuery()
|
||||
.eq(ProcessConfig::getStatus, 1)
|
||||
.eq(ProcessConfig::getIsDeleted, 0)
|
||||
.like(ProcessConfig::getProjectIds, projectIdStr)
|
||||
.orderByDesc(ProcessConfig::getUpdateTime)
|
||||
.orderByDesc(ProcessConfig::getCreateTime))
|
||||
.stream()
|
||||
.filter(config -> containsProjectId(config.getProjectIds(), projectIdStr))
|
||||
.map(ProcessConfig::getNodeConfigJson)
|
||||
.filter(Func::isNotEmpty)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 一批项目的生效配置:项目主键 → 节点配置 JSON,一次查全再按项目匹配,供列表页避免逐条回查
|
||||
*
|
||||
* @param service 过程配置服务
|
||||
* @param projectIds 项目主键集合,允许含 null
|
||||
*/
|
||||
public static Map<Long, String> byProjects(IProcessConfigService service, Collection<Long> projectIds) {
|
||||
Map<Long, String> live = new LinkedHashMap<>();
|
||||
if (service == null || Func.isEmpty(projectIds)) {
|
||||
return live;
|
||||
}
|
||||
Set<Long> wanted = new LinkedHashSet<>();
|
||||
projectIds.stream().filter(Objects::nonNull).forEach(wanted::add);
|
||||
if (wanted.isEmpty()) {
|
||||
return live;
|
||||
}
|
||||
List<ProcessConfig> configs = service.list(Wrappers.<ProcessConfig>lambdaQuery()
|
||||
.eq(ProcessConfig::getStatus, 1)
|
||||
.eq(ProcessConfig::getIsDeleted, 0)
|
||||
.orderByDesc(ProcessConfig::getUpdateTime)
|
||||
.orderByDesc(ProcessConfig::getCreateTime));
|
||||
if (Func.isEmpty(configs)) {
|
||||
return live;
|
||||
}
|
||||
for (Long projectId : wanted) {
|
||||
String projectIdStr = String.valueOf(projectId);
|
||||
for (ProcessConfig config : configs) {
|
||||
if (Func.isEmpty(config.getNodeConfigJson())
|
||||
|| !containsProjectId(config.getProjectIds(), projectIdStr)) {
|
||||
continue;
|
||||
}
|
||||
live.put(projectId, config.getNodeConfigJson());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return live;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生效配置优先,取不到时回退运单快照
|
||||
*
|
||||
* @param liveJson 生效配置 JSON,可为空
|
||||
* @param snapshot 运单快照 JSON,可为空
|
||||
*/
|
||||
public static String resolve(String liveJson, String snapshot) {
|
||||
return Func.isNotEmpty(liveJson) ? liveJson : snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
* 生效配置优先;生效配置没有可打卡节点时回退快照,两边都没有节点就按生效配置来
|
||||
*
|
||||
* @param liveJson 生效配置 JSON,可为空
|
||||
* @param snapshot 运单快照 JSON,可为空
|
||||
*/
|
||||
public static String resolveForPunch(String liveJson, String snapshot) {
|
||||
if (Func.isEmpty(liveJson)) {
|
||||
return snapshot;
|
||||
}
|
||||
if (!WaybillProcessSupport.listDriverPunchNodes(liveJson).isEmpty()) {
|
||||
return liveJson;
|
||||
}
|
||||
if (Func.isNotEmpty(snapshot) && !WaybillProcessSupport.listDriverPunchNodes(snapshot).isEmpty()) {
|
||||
return snapshot;
|
||||
}
|
||||
return liveJson;
|
||||
}
|
||||
|
||||
/** 项目集合是逗号串,必须按逗号分段精确比对 */
|
||||
private static boolean containsProjectId(String projectIds, String projectId) {
|
||||
if (Func.isEmpty(projectIds) || Func.isEmpty(projectId)) {
|
||||
return false;
|
||||
}
|
||||
return Arrays.stream(projectIds.split(","))
|
||||
.map(String::trim)
|
||||
.anyMatch(projectId::equals);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user