fix(order): paginate merchant workbench with scoped queue counts

This commit is contained in:
2026-10-03 14:57:51 +08:00
parent 7d9ee18196
commit 780d37e1d2
22 changed files with 705 additions and 6 deletions
@@ -40,9 +40,20 @@ class InternalInboundOrderReferenceControllerTest {
@MockBean
private OrderRemarksSyncService remarksSync;
@MockBean private com.baiyue.firstleg.service.OrderFulfillmentSnapshotService fulfillmentSnapshot;
@MockBean private com.baiyue.firstleg.service.MerchantWorkbenchFilterService workbenchFilters;
@Resource
private InternalInboundOrderController controller;
@Test void workbenchFiltersRejectMissingAndWrongInternalIdentity() {
var request = new com.baiyue.firstleg.dto.MerchantWorkbenchFilters.Request(11L, "LOADED");
assertThatThrownBy(() -> controller.workbenchFilters(null, request)).isInstanceOf(BizException.class);
assertThatThrownBy(() -> controller.workbenchFilters("wrong", request)).isInstanceOf(BizException.class);
org.mockito.Mockito.verifyNoInteractions(workbenchFilters);
var response = new com.baiyue.firstleg.dto.MerchantWorkbenchFilters.Response(List.of(1L), List.of(), List.of());
when(workbenchFilters.read(request)).thenReturn(response);
assertThat(controller.workbenchFilters("internal-token", request).data()).isEqualTo(response);
}
@Test
void completionSnapshotRequiresInternalTokenAndDelegatesExactCommand() {
var command = new com.baiyue.firstleg.dto.OrderFulfillmentCompleted(11, 21,
@@ -0,0 +1,80 @@
package com.baiyue.firstleg.repository;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.baiyue.firstleg.mapper.MerchantWorkbenchFilterMapper;
import org.h2.jdbcx.JdbcDataSource;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.jdbc.core.JdbcTemplate;
import java.util.List;
import java.util.UUID;
import static org.assertj.core.api.Assertions.assertThat;
class MerchantWorkbenchFilterMapperTest {
private JdbcTemplate jdbc;
private MerchantWorkbenchFilterMapper mapper;
@BeforeEach void setup() throws Exception {
var ds=new JdbcDataSource();ds.setURL("jdbc:h2:mem:workbench-facts-"+UUID.randomUUID()+";MODE=MySQL;DB_CLOSE_DELAY=-1");
jdbc=new JdbcTemplate(ds);
jdbc.execute("CREATE TABLE fl_inbound_order(id BIGINT PRIMARY KEY,tenant_id BIGINT,order_id BIGINT,deleted INT DEFAULT 0,tax_mode VARCHAR(32),process_status VARCHAR(32))");
jdbc.execute("CREATE TABLE fl_inbound_fee(id BIGINT PRIMARY KEY,tenant_id BIGINT,inbound_id BIGINT,fee_version INT,fee_status VARCHAR(32),customer_confirmed_at TIMESTAMP)");
jdbc.execute("CREATE TABLE fl_loading_plan(id BIGINT PRIMARY KEY,container_booking_id BIGINT,plan_status VARCHAR(32),confirmed_at TIMESTAMP,updated_at TIMESTAMP)");
jdbc.execute("CREATE TABLE fl_loading_plan_item(loading_plan_id BIGINT,inbound_id BIGINT,account_id BIGINT,deleted INT DEFAULT 0)");
jdbc.execute("CREATE TABLE fl_container_booking(id BIGINT PRIMARY KEY,record_status VARCHAR(32))");
jdbc.execute("CREATE TABLE fl_customs_container(id BIGINT PRIMARY KEY,loading_plan_id BIGINT,status VARCHAR(40))");
jdbc.execute("CREATE TABLE fl_customs_customer_document(id BIGINT PRIMARY KEY,customs_container_id BIGINT,tenant_id BIGINT,status VARCHAR(32))");
jdbc.execute("CREATE TABLE fl_customs_customer_document_inbound(document_id BIGINT,inbound_id BIGINT,tenant_id BIGINT)");
var config=new MybatisConfiguration();config.setMapUnderscoreToCamelCase(true);config.addMapper(MerchantWorkbenchFilterMapper.class);
var factory=new MybatisSqlSessionFactoryBean();factory.setDataSource(ds);factory.setConfiguration(config);
mapper=new SqlSessionTemplate(factory.getObject()).getMapper(MerchantWorkbenchFilterMapper.class);
jdbc.update("INSERT INTO fl_inbound_order VALUES(1,11,101,0,'TAX_REFUND','LOADED'),(2,11,102,0,'TAX_REFUND','DELIVERED'),(3,22,103,0,'TAX_REFUND','LOADED'),(4,11,104,1,'TAX_REFUND','LOADED'),(5,11,105,0,'BUY_DECLARATION','LOADED')");
jdbc.update("INSERT INTO fl_container_booking VALUES(100,'ACTIVE')");
jdbc.update("INSERT INTO fl_loading_plan VALUES(100,100,'COMPLETED',CURRENT_TIMESTAMP,CURRENT_TIMESTAMP)");
jdbc.update("INSERT INTO fl_loading_plan_item VALUES(100,1,11,0),(100,1,11,0),(100,2,11,0),(100,3,22,0),(100,4,11,0),(100,5,11,0)");
}
@Test void latestFeeVersionAndCustomerScopeDetermineConfirmationQueue() {
jdbc.update("INSERT INTO fl_inbound_fee VALUES(10,11,1,1,'SENT',NULL),(11,11,1,2,'DRAFT',NULL),(12,11,2,1,'SENT',NULL),(13,22,3,1,'SENT',NULL),(14,11,4,1,'SENT',NULL)");
assertThat(mapper.pendingFees(11)).containsExactly(102L);
jdbc.update("INSERT INTO fl_inbound_fee VALUES(15,11,2,1,'SENT',CURRENT_TIMESTAMP)");
assertThat(mapper.pendingFees(11)).isEmpty();
assertThat(mapper.pendingFees(22)).containsExactly(103L);
assertThat(mapper.byProcessStatuses(11,List.of("LOADED"))).containsExactly(101L,105L);
}
@Test void absentAggregateIsReadOnlyAndWaitingForOwnDocumentsNotAnotherCustomer() {
assertThat(mapper.pendingCustoms(11)).containsExactly(101L,102L);
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM fl_customs_container",Integer.class)).isZero();
jdbc.update("INSERT INTO fl_customs_container VALUES(200,100,'WAITING_CUSTOMER_DOCUMENTS')");
jdbc.update("INSERT INTO fl_customs_customer_document VALUES(201,200,11,'ACTIVE')");
jdbc.update("INSERT INTO fl_customs_customer_document_inbound VALUES(201,1,11)");
assertThat(mapper.pendingCustoms(11)).containsExactly(101L,102L);
jdbc.update("INSERT INTO fl_customs_customer_document_inbound VALUES(201,2,11)");
assertThat(mapper.pendingCustoms(11)).isEmpty();
assertThat(mapper.pendingCustoms(22)).containsExactly(103L);
}
@Test void foreignOrDeletedDocumentsCannotSatisfyOwnCoverageAndClosedContainersAreExcluded() {
jdbc.update("INSERT INTO fl_customs_container VALUES(200,100,'WAITING_CUSTOMER_DOCUMENTS')");
jdbc.update("INSERT INTO fl_customs_customer_document VALUES(201,200,22,'ACTIVE'),(202,200,11,'DELETED')");
jdbc.update("INSERT INTO fl_customs_customer_document_inbound VALUES(201,1,11),(202,2,11)");
assertThat(mapper.pendingCustoms(11)).containsExactly(101L,102L);
for(String status:List.of("READY_TO_START","DECLARING","COMPLETED")) {
jdbc.update("UPDATE fl_customs_container SET status=?",status);
assertThat(mapper.pendingCustoms(11)).isEmpty();
}
}
@Test void onlyLatestCompletedPlanCanCreateTheCurrentUploadQueue() {
jdbc.update("INSERT INTO fl_customs_container VALUES(200,100,'WAITING_CUSTOMER_DOCUMENTS')");
jdbc.update("INSERT INTO fl_container_booking VALUES(101,'ACTIVE')");
jdbc.update("INSERT INTO fl_loading_plan VALUES(101,101,'COMPLETED',TIMESTAMP '2099-01-01 00:00:00',CURRENT_TIMESTAMP)");
jdbc.update("INSERT INTO fl_loading_plan_item VALUES(101,1,11,0)");
jdbc.update("INSERT INTO fl_customs_container VALUES(201,101,'COMPLETED')");
assertThat(mapper.pendingCustoms(11)).containsExactly(102L);
jdbc.update("UPDATE fl_container_booking SET record_status='CANCELLED' WHERE id=100");
assertThat(mapper.pendingCustoms(11)).isEmpty();
}
}