提交
This commit is contained in:
@@ -23,6 +23,7 @@ public interface MerchantWorkbenchFilterMapper {
|
||||
FROM fl_inbound_order i
|
||||
JOIN fl_inbound_fee f ON f.tenant_id=i.tenant_id AND f.inbound_id=i.id
|
||||
WHERE i.tenant_id=#{tenantId} AND i.deleted=0 AND i.order_id IS NOT NULL
|
||||
AND i.process_status != 'CANCELLED' AND i.measure_review_status='APPROVED'
|
||||
AND f.fee_status='SENT' AND f.customer_confirmed_at IS NULL
|
||||
AND NOT EXISTS (SELECT 1 FROM fl_inbound_fee newer
|
||||
WHERE newer.tenant_id=f.tenant_id AND newer.inbound_id=f.inbound_id
|
||||
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.baiyue.firstleg.repository;
|
||||
|
||||
import com.baiyue.firstleg.dto.MerchantWorkbenchFilters;
|
||||
import com.baiyue.firstleg.mapper.MerchantWorkbenchFilterMapper;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.util.List;
|
||||
|
||||
/** 客户工作台筛选事实的持久化入口。 */
|
||||
@Repository
|
||||
public class MerchantWorkbenchFilterRepository {
|
||||
private final MerchantWorkbenchFilterMapper mapper;
|
||||
|
||||
/** 注入客户隔离的筛选查询。 */
|
||||
public MerchantWorkbenchFilterRepository(MerchantWorkbenchFilterMapper mapper) {
|
||||
this.mapper = mapper;
|
||||
}
|
||||
|
||||
/** 查询物流主状态对应的订单范围。 */
|
||||
public List<Long> byProcessStatuses(long tenantId, List<String> statuses) {
|
||||
return mapper.byProcessStatuses(tenantId, statuses);
|
||||
}
|
||||
|
||||
/** 只返回最新已推送费用仍待客户确认的订单。 */
|
||||
public List<Long> pendingFees(long tenantId) {
|
||||
return mapper.pendingFees(tenantId);
|
||||
}
|
||||
|
||||
/** 查询待客户补齐报关资料的订单。 */
|
||||
public List<Long> pendingCustoms(long tenantId) {
|
||||
return mapper.pendingCustoms(tenantId);
|
||||
}
|
||||
|
||||
/** 查询客户订单的实际履约片段。 */
|
||||
public List<MerchantWorkbenchFilters.Fragment> progressFragments(long tenantId, List<Long> orderIds) {
|
||||
return mapper.progressFragments(tenantId, orderIds);
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -1,30 +1,30 @@
|
||||
package com.baiyue.firstleg.service;
|
||||
|
||||
import com.baiyue.firstleg.dto.MerchantWorkbenchFilters;
|
||||
import com.baiyue.firstleg.mapper.MerchantWorkbenchFilterMapper;
|
||||
import com.baiyue.firstleg.repository.MerchantWorkbenchFilterRepository;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class MerchantWorkbenchFilterService {
|
||||
@Resource private MerchantWorkbenchFilterMapper mapper;
|
||||
@Resource private MerchantWorkbenchFilterRepository repository;
|
||||
@Resource private UnifiedInboundQueryService unified;
|
||||
@Resource private com.baiyue.firstleg.workflow.ChargeableWeightService weights;
|
||||
|
||||
/** 精确详情只读进度;工作台待确认队列使用计费重事实,金额确认不能代替。 */
|
||||
/** 精确详情只读进度;确认计费重待办统一使用最新已推送费用的客户确认状态。 */
|
||||
@org.springframework.transaction.annotation.Transactional(readOnly=true)
|
||||
public MerchantWorkbenchFilters.Response read(MerchantWorkbenchFilters.Request request) {
|
||||
if (request.orderIds() != null) {
|
||||
return new MerchantWorkbenchFilters.Response(null, java.util.List.of(), java.util.List.of(),
|
||||
progress(mapper.progressFragments(request.tenantId(), request.orderIds())));
|
||||
progress(repository.progressFragments(request.tenantId(), request.orderIds())));
|
||||
}
|
||||
return new MerchantWorkbenchFilters.Response(
|
||||
request.mainStatus() == null ? null : mapper.byProcessStatuses(request.tenantId(),
|
||||
request.mainStatus() == null ? null : repository.byProcessStatuses(request.tenantId(),
|
||||
unified.internalStatuses(request.mainStatus())),
|
||||
weights.pendingOrders(request.tenantId()), mapper.pendingCustoms(request.tenantId()),
|
||||
progress(mapper.progressFragments(request.tenantId(), null)));
|
||||
repository.pendingFees(request.tenantId()), repository.pendingCustoms(request.tenantId()),
|
||||
progress(repository.progressFragments(request.tenantId(), null)));
|
||||
}
|
||||
|
||||
/** 将实装片段聚合为订单级履约进度。 */
|
||||
static java.util.List<MerchantWorkbenchFilters.Progress> progress(java.util.List<MerchantWorkbenchFilters.Fragment> facts) {
|
||||
var grouped = facts.stream().collect(java.util.stream.Collectors.groupingBy(MerchantWorkbenchFilters.Fragment::orderId));
|
||||
return grouped.entrySet().stream().sorted(java.util.Map.Entry.comparingByKey()).map(entry -> {
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
package com.baiyue.firstleg.workflow;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import java.util.*;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.LocalDateTime;
|
||||
/** 客户独立确认计费重,金额确认和主流程不受影响。 */
|
||||
@Service
|
||||
public class ChargeableWeightService {
|
||||
private final WorkflowFactRepository repository;
|
||||
/** 注入当前事实和确认历史。 */
|
||||
public ChargeableWeightService(WorkflowFactRepository repository) { this.repository=repository; }
|
||||
/** 客户仅能读取本订单已审核的计费重。 */
|
||||
public List<WorkflowFacts.Weight> read(long tenant,long orderId) {
|
||||
return repository.facts(repository.orderInbounds(tenant,orderId)).stream().filter(f->f.tenantId()==tenant && eligible(f)).map(ChargeableWeightService::weight).toList();
|
||||
}
|
||||
/** 客户工作台按独立确认事实计算待办,不借用费用确认。 */
|
||||
public List<Long> pendingOrders(long tenant) {
|
||||
var ids=repository.weightCandidates(tenant);var orders=new LinkedHashSet<Long>();
|
||||
for(int offset=0;offset<ids.size();offset+=500) for(var f:repository.facts(ids.subList(offset,Math.min(offset+500,ids.size()))))
|
||||
if(f.tenantId()==tenant && eligible(f) && !weight(f).confirmed()) orders.add(f.orderId());
|
||||
return List.copyOf(orders);
|
||||
}
|
||||
/** 值和测量轮次变化使旧确认失效,金额变化不影响计费重。 */
|
||||
public static String fingerprint(WorkflowFacts.Fact f) {
|
||||
try { String value=f.inboundId()+"|"+f.chargeableWeightKg().stripTrailingZeros().toPlainString()+"|"+f.measureCompletedAt();
|
||||
return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(value.getBytes(StandardCharsets.UTF_8)));
|
||||
} catch(java.security.NoSuchAlgorithmException e) { throw new IllegalStateException(e); }
|
||||
}
|
||||
/** 未审核或非正数不允许确认。 */
|
||||
public static boolean eligible(WorkflowFacts.Fact f) { return "APPROVED".equals(f.reviewStatus()) && f.chargeableWeightKg()!=null && f.chargeableWeightKg().signum()>0; }
|
||||
/** 只认可当前版本的确认。 */
|
||||
public static WorkflowFacts.Weight weight(WorkflowFacts.Fact f) {
|
||||
String hash=fingerprint(f);boolean confirmed=hash.equals(f.confirmationFingerprint());
|
||||
return new WorkflowFacts.Weight(f.inboundId(),f.orderId(),f.chargeableWeightKg(),hash,confirmed,confirmed?f.confirmedAt():null);
|
||||
}
|
||||
/** 入仓锁、预期版本和幂等键防止确认旧值或异参重放。 */
|
||||
@Transactional
|
||||
public WorkflowFacts.Weight confirm(long tenant,long operator,long id,String key,WorkflowFacts.Confirm request) {
|
||||
if(key==null || key.isBlank() || key.length()>128) throw new BizException(ResultCode.BAD_REQUEST,"确认幂等键不能为空且最多128字符");
|
||||
var inbound=repository.lock(tenant,id);
|
||||
if(inbound==null || "CANCELLED".equals(inbound.getProcessStatus())) throw new BizException(ResultCode.NOT_FOUND,"入仓单不存在");
|
||||
var replay=repository.command(tenant,operator,key);
|
||||
if(replay!=null && (replay.inboundId()!=id || !replay.fingerprint().equals(request.expectedFingerprint()))) throw new BizException(ResultCode.CONFLICT,"同一幂等键不能确认不同计费重");
|
||||
var facts=repository.facts(List.of(id));
|
||||
if(facts.isEmpty()) throw new BizException(ResultCode.CONFLICT,"请分别确认分柜后的独立订单计费重");
|
||||
var f=facts.get(0);
|
||||
if(!eligible(f)) throw new BizException(ResultCode.CONFLICT,"计费重尚未审核通过");
|
||||
var current=weight(f);
|
||||
if(!current.fingerprint().equals(request.expectedFingerprint())) throw new BizException(ResultCode.CONFLICT,"计费重已变化,请刷新后重新确认");
|
||||
if(replay==null) repository.insert(new WorkflowFacts.Confirmation(com.baomidou.mybatisplus.core.toolkit.IdWorker.getId(),tenant,id,operator,key,current.fingerprint(),current.chargeableWeightKg(),LocalDateTime.now()));
|
||||
return weight(repository.facts(List.of(id)).get(0));
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package com.baiyue.firstleg.workflow;
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.firstleg.service.FirstlegScopeGuard;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import jakarta.validation.Valid;
|
||||
import java.util.List;
|
||||
/** 客户计费重确认入口,独立于费用确认。 */
|
||||
@RestController
|
||||
@RequestMapping("/api/merchant/v1/first-leg")
|
||||
public class MerchantChargeableWeightController {
|
||||
private final ChargeableWeightService service;
|
||||
/** 注入独立确认服务。 */
|
||||
public MerchantChargeableWeightController(ChargeableWeightService service) { this.service=service; }
|
||||
/** 客户范围取认证上下文。 */
|
||||
@GetMapping("/orders/{orderId}/chargeable-weights")
|
||||
@PreAuthorize("hasAuthority('merchant:order:view')")
|
||||
public ApiResponse<List<WorkflowFacts.Weight>> read(@AuthenticationPrincipal AuthenticatedUser user,@PathVariable long orderId) { return ApiResponse.success(service.read(FirstlegScopeGuard.merchant(user),orderId)); }
|
||||
/** 不触发费用确认与金额锁定。 */
|
||||
@com.baiyue.common.audit.BusinessAudit(event="CUSTOMER_CHARGEABLE_WEIGHT_CONFIRM",resourceType="INBOUND_ORDER",resourceId="#inboundId",description="客户独立确认计费重")
|
||||
@PostMapping("/chargeable-weights/{inboundId}/confirm")
|
||||
@PreAuthorize("hasAuthority('merchant:order:edit')")
|
||||
public ApiResponse<WorkflowFacts.Weight> confirm(@AuthenticationPrincipal AuthenticatedUser user,@PathVariable long inboundId,@RequestHeader("Idempotency-Key") String key,@Valid @RequestBody WorkflowFacts.Confirm request) { return ApiResponse.success(service.confirm(FirstlegScopeGuard.merchant(user),user.userId(),inboundId,key,request)); }
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.baiyue.firstleg.workflow;
|
||||
import org.apache.ibatis.annotations.*;
|
||||
import java.util.List;
|
||||
/** 柜事实和独立确认SQL入口。 */
|
||||
/** 柜事实与最新费用确认状态的只读 SQL 入口。 */
|
||||
@Mapper
|
||||
public interface WorkflowFactMapper {
|
||||
String ACTUAL="""
|
||||
@@ -13,29 +13,29 @@ public interface WorkflowFactMapper {
|
||||
AND pk.item_status IN ('PICKED','PARTIAL') AND pk.picked_package_count>0
|
||||
""";
|
||||
/** 按独立实装票核验资料;来源单承接实装时,余量子单不能将来源票排除。 */
|
||||
@Select("<script>SELECT i.tenant_id,i.id inbound_id,i.order_id,i.measure_review_status review_status,i.chargeable_weight_kg,i.measure_completed_at,"
|
||||
@Select("<script>SELECT i.tenant_id,i.id inbound_id,i.order_id,i.measure_review_status review_status,"
|
||||
+"EXISTS(SELECT 1 "+ACTUAL+" AND EXISTS(SELECT 1 FROM fl_carrier_schedule_node_fact n WHERE n.container_booking_id=b.id AND n.standard_node='DEPARTED_ORIGIN_PORT' AND n.actual_at IS NOT NULL)) departed,"
|
||||
+"EXISTS(SELECT 1 "+ACTUAL+" AND EXISTS(SELECT 1 FROM fl_container_document d WHERE d.container_booking_id=b.id AND d.document_type='DESTINATION_CLEARANCE' AND d.file_id IS NOT NULL)) clearance_uploaded,"
|
||||
+"EXISTS(SELECT 1 "+ACTUAL+" AND i.tax_mode='TAX_REFUND' AND NOT EXISTS(SELECT 1 FROM fl_customs_customer_document_inbound s JOIN fl_customs_customer_document d ON d.id=s.document_id AND d.tenant_id=i.tenant_id AND d.status='ACTIVE' JOIN fl_customs_container c ON c.id=d.customs_container_id AND c.loading_plan_id=p.id WHERE s.inbound_id=i.id AND s.tenant_id=i.tenant_id)) missing_customs,"
|
||||
+"EXISTS(SELECT 1 "+ACTUAL+" AND (i.tax_mode='BUY_DECLARATION' OR EXISTS(SELECT 1 FROM fl_customs_customer_document_inbound s JOIN fl_customs_customer_document d ON d.id=s.document_id AND d.tenant_id=i.tenant_id AND d.status='ACTIVE' JOIN fl_customs_container c ON c.id=d.customs_container_id AND c.loading_plan_id=p.id WHERE s.inbound_id=i.id AND s.tenant_id=i.tenant_id))) export_customs_ready,"
|
||||
+"(SELECT w.fingerprint FROM fl_chargeable_weight_confirmation w WHERE w.tenant_id=i.tenant_id AND w.inbound_id=i.id ORDER BY w.id DESC LIMIT 1) confirmation_fingerprint,"
|
||||
+"(SELECT w.confirmed_at FROM fl_chargeable_weight_confirmation w WHERE w.tenant_id=i.tenant_id AND w.inbound_id=i.id ORDER BY w.id DESC LIMIT 1) confirmed_at "
|
||||
+"EXISTS(SELECT 1 "+ACTUAL+" AND (i.tax_mode='BUY_DECLARATION' OR EXISTS(SELECT 1 FROM fl_customs_customer_document_inbound s JOIN fl_customs_customer_document d ON d.id=s.document_id AND d.tenant_id=i.tenant_id AND d.status='ACTIVE' JOIN fl_customs_container c ON c.id=d.customs_container_id AND c.loading_plan_id=p.id WHERE s.inbound_id=i.id AND s.tenant_id=i.tenant_id))) export_customs_ready "
|
||||
+"FROM fl_inbound_order i WHERE i.deleted=0 AND NOT EXISTS(SELECT 1 FROM fl_inbound_order child WHERE child.tenant_id=i.tenant_id AND child.parent_inbound_id=i.id AND child.inbound_role='CHILD' AND (child.split_purpose IS NULL OR child.split_purpose != 'REMAINDER') AND child.deleted=0) AND i.process_status != 'CANCELLED' AND i.id IN "
|
||||
+"<foreach collection='ids' item='id' open='(' separator=',' close=')'>#{id}</foreach></script>")
|
||||
List<WorkflowFacts.Fact> facts(@Param("ids") List<Long> ids);
|
||||
/** 锁定客户自己的入仓单,串行确认与测量写入。 */
|
||||
@Select("SELECT * FROM fl_inbound_order WHERE tenant_id=#{tenantId} AND id=#{id} AND deleted=0 FOR UPDATE")
|
||||
com.baiyue.firstleg.entity.InboundOrderEntity lock(@Param("tenantId") long tenantId,@Param("id") long id);
|
||||
/** 客户订单下全部独立入仓单。 */
|
||||
@Select("SELECT id FROM fl_inbound_order WHERE tenant_id=#{tenantId} AND order_id=#{orderId} AND deleted=0 AND process_status != 'CANCELLED' ORDER BY id")
|
||||
List<Long> orderInbounds(@Param("tenantId") long tenantId,@Param("orderId") long orderId);
|
||||
/** 客户工作台只读取审核通过且有正计费重的来源。 */
|
||||
@Select("SELECT id FROM fl_inbound_order WHERE tenant_id=#{tenantId} AND deleted=0 AND process_status != 'CANCELLED' AND measure_review_status='APPROVED' AND chargeable_weight_kg>0 ORDER BY id")
|
||||
List<Long> weightCandidates(@Param("tenantId") long tenantId);
|
||||
/** 成员幂等键不允许跨确认对象复用。 */
|
||||
@Select("SELECT * FROM fl_chargeable_weight_confirmation WHERE tenant_id=#{tenantId} AND operator_id=#{operatorId} AND command_key=#{key}")
|
||||
WorkflowFacts.Confirmation command(@Param("tenantId") long tenantId,@Param("operatorId") long operatorId,@Param("key") String key);
|
||||
/** 独立记录,不修改费用或物流主状态。 */
|
||||
@Insert("INSERT INTO fl_chargeable_weight_confirmation(id,tenant_id,inbound_id,operator_id,command_key,fingerprint,chargeable_weight_kg,confirmed_at) VALUES(#{id},#{tenantId},#{inboundId},#{operatorId},#{commandKey},#{fingerprint},#{chargeableWeightKg},#{confirmedAt})")
|
||||
int insert(WorkflowFacts.Confirmation value);
|
||||
/** 客户确认页签只读取最新已推送费用版本,旧版本确认不能覆盖新版本。 */
|
||||
@Select("""
|
||||
<script>
|
||||
SELECT i.tenant_id, i.order_id,
|
||||
CASE WHEN f.customer_confirmed_at IS NULL THEN 'WAIT_WEIGHT_CONFIRM'
|
||||
ELSE 'WEIGHT_CONFIRMED' END AS main_status
|
||||
FROM fl_inbound_order i
|
||||
JOIN fl_inbound_fee f ON f.tenant_id=i.tenant_id AND f.inbound_id=i.id
|
||||
WHERE i.deleted=0 AND i.process_status != 'CANCELLED'
|
||||
AND i.measure_review_status='APPROVED' AND f.fee_status='SENT'
|
||||
AND NOT EXISTS (SELECT 1 FROM fl_inbound_fee newer
|
||||
WHERE newer.tenant_id=f.tenant_id AND newer.inbound_id=f.inbound_id
|
||||
AND (newer.fee_version>f.fee_version OR (newer.fee_version=f.fee_version AND newer.id>f.id)))
|
||||
AND i.id IN <foreach collection='ids' item='id' open='(' separator=',' close=')'>#{id}</foreach>
|
||||
</script>
|
||||
""")
|
||||
List<com.baiyue.firstleg.service.UnifiedInboundQueryService.StatusRow> feeConfirmationRows(@Param("ids") List<Long> ids);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.baiyue.firstleg.workflow;
|
||||
import org.springframework.stereotype.Repository;
|
||||
import java.util.List;
|
||||
/** 本地柜事实与确认记录的持久化入口。 */
|
||||
/** 本地柜事实与费用确认状态的只读持久化入口。 */
|
||||
@Repository
|
||||
public class WorkflowFactRepository {
|
||||
private final WorkflowFactMapper mapper;
|
||||
@@ -9,14 +9,8 @@ public class WorkflowFactRepository {
|
||||
public WorkflowFactRepository(WorkflowFactMapper mapper) { this.mapper=mapper; }
|
||||
/** 空候选不读取数据。 */
|
||||
public List<WorkflowFacts.Fact> facts(List<Long> ids) { return ids.isEmpty()?List.of():mapper.facts(ids); }
|
||||
/** 锁定所属客户来源。 */
|
||||
public com.baiyue.firstleg.entity.InboundOrderEntity lock(long tenant,long id) { return mapper.lock(tenant,id); }
|
||||
/** 查询订单下独立来源。 */
|
||||
public List<Long> orderInbounds(long tenant,long order) { return mapper.orderInbounds(tenant,order); }
|
||||
/** 客户计费重待确认候选。 */
|
||||
public List<Long> weightCandidates(long tenant) { return mapper.weightCandidates(tenant); }
|
||||
/** 读取成员确认命令。 */
|
||||
public WorkflowFacts.Confirmation command(long tenant,long operator,String key) { return mapper.command(tenant,operator,key); }
|
||||
/** 追加确认历史。 */
|
||||
public void insert(WorkflowFacts.Confirmation value) { mapper.insert(value); }
|
||||
/** 按本轮有效入仓范围读取最新推送费用的客户确认状态。 */
|
||||
public List<com.baiyue.firstleg.service.UnifiedInboundQueryService.StatusRow> feeConfirmationRows(List<Long> ids) {
|
||||
return ids.isEmpty() ? List.of() : mapper.feeConfirmationRows(ids);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
package com.baiyue.firstleg.workflow;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
/** 页签事实与独立计费重确认契约。 */
|
||||
/** 订单业务页签的只读事实契约。 */
|
||||
public final class WorkflowFacts {
|
||||
/** 仅承载查询及确认契约,不允许实例化。 */
|
||||
/** 仅承载事实查询契约,不允许实例化。 */
|
||||
private WorkflowFacts() {}
|
||||
public record Fact(long tenantId,long inboundId,long orderId,String reviewStatus,BigDecimal chargeableWeightKg,LocalDateTime measureCompletedAt,
|
||||
boolean departed,boolean clearanceUploaded,boolean missingCustoms,boolean exportCustomsReady,String confirmationFingerprint,LocalDateTime confirmedAt) {}
|
||||
public record Fact(long tenantId,long inboundId,long orderId,String reviewStatus,
|
||||
boolean departed,boolean clearanceUploaded,boolean missingCustoms,boolean exportCustomsReady) {}
|
||||
public record Source(long tenantId,long inboundId) {}
|
||||
public record Request(List<Source> sources) {}
|
||||
public record Overseas(long tenantId,long inboundId,boolean outboundExists,boolean pendingOutbound,boolean delivering,boolean unloaded) {}
|
||||
public record Weight(long inboundId,long orderId,BigDecimal chargeableWeightKg,String fingerprint,boolean confirmed,LocalDateTime confirmedAt) {}
|
||||
public record Confirm(@jakarta.validation.constraints.Pattern(regexp="[a-f0-9]{64}") @jakarta.validation.constraints.NotNull String expectedFingerprint) {}
|
||||
public record Confirmation(long id,long tenantId,long inboundId,long operatorId,String commandKey,String fingerprint,BigDecimal chargeableWeightKg,LocalDateTime confirmedAt) {}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ public class WorkflowTabService {
|
||||
var batch=inbounds.subList(offset,Math.min(offset+500,inbounds.size()));
|
||||
var expected=new HashMap<Long,InboundOrderEntity>();batch.forEach(i->expected.put(i.getId(),i));
|
||||
var facts=repository.facts(batch.stream().map(InboundOrderEntity::getId).toList());
|
||||
// 确认页签复用已推送费用版本。
|
||||
for(var fee:repository.feeConfirmationRows(facts.stream().map(WorkflowFacts.Fact::inboundId).toList())) {
|
||||
if(facts.stream().noneMatch(f->f.tenantId()==fee.tenantId() && f.orderId()==fee.orderId())) throw unavailable();
|
||||
result.putIfAbsent(fee.orderId()+":"+fee.mainStatus(),fee);
|
||||
}
|
||||
var sources=facts.stream().filter(WorkflowFacts.Fact::departed).map(f->new WorkflowFacts.Source(f.tenantId(),f.inboundId())).distinct().toList();
|
||||
Map<String,WorkflowFacts.Overseas> overseas=new HashMap<>();
|
||||
if(!sources.isEmpty()) {
|
||||
@@ -46,11 +51,10 @@ public class WorkflowTabService {
|
||||
}
|
||||
return List.copyOf(result.values());
|
||||
}
|
||||
/** 分类为可重叠的待办/证据集合,计费重确认独立于费用确认。 */
|
||||
/** 分类为可重叠的物流待办/证据集合,客户确认由最新费用版本单独提供。 */
|
||||
public static Set<String> classify(WorkflowFacts.Fact f,String process,WorkflowFacts.Overseas o) {
|
||||
Set<String> keys=new LinkedHashSet<>();
|
||||
if("MEASURE_PENDING".equals(process) && "PENDING".equals(f.reviewStatus())) keys.add("MEASUREMENT_REVIEW");
|
||||
if(ChargeableWeightService.eligible(f)) keys.add(ChargeableWeightService.weight(f).confirmed()?"WEIGHT_CONFIRMED":"WAIT_WEIGHT_CONFIRM");
|
||||
boolean signed="DELIVERED".equals(process);
|
||||
// 报关页签按资料与实装事实汇总,不依赖点击开始报关;实际开船后转入清关链路。
|
||||
if(!f.departed() && !signed) {
|
||||
|
||||
Reference in New Issue
Block a user