This commit is contained in:
2026-08-25 14:46:06 +08:00
parent 5abc7543ae
commit f325d57fc1
106 changed files with 3682 additions and 450 deletions
@@ -72,11 +72,25 @@ public interface LoadingPickItemMapper extends BaseMapper<LoadingPickItemEntity>
@Param("operatorName") String operatorName);
@Select("""
SELECT COALESCE(SUM(plan_item.package_count),0) expectedCount,
SELECT COALESCE(SUM(CASE
WHEN pick_item.item_status='SKIPPED' AND pick_item.picked_package_count=0 THEN 0
WHEN pick_item.item_status='PARTIAL' AND pick_item.picked_package_count>0
AND pick_item.picked_package_count<=plan_item.package_count
THEN pick_item.picked_package_count
ELSE plan_item.package_count
END),0) expectedCount,
COALESCE(SUM(pick_item.picked_package_count),0) pickedCount,
COUNT(DISTINCT pick_task.id) taskCount,
COUNT(DISTINCT CASE WHEN pick_task.task_status='COMPLETED' THEN pick_task.id END) completedTaskCount,
SUM(CASE WHEN pick_item.id IS NULL OR pick_item.picked_package_count<>plan_item.package_count THEN 1 ELSE 0 END) unresolvedCount
SUM(CASE
WHEN pick_item.id IS NULL THEN 1
WHEN pick_item.item_status='PICKED'
AND pick_item.picked_package_count=plan_item.package_count THEN 0
WHEN pick_item.item_status='PARTIAL' AND pick_item.picked_package_count>0
AND pick_item.picked_package_count<=plan_item.package_count THEN 0
WHEN pick_item.item_status='SKIPPED' AND pick_item.picked_package_count=0 THEN 0
ELSE 1
END) unresolvedCount
FROM fl_loading_plan_item plan_item
JOIN fl_inbound_order inbound_order ON inbound_order.id=plan_item.inbound_id
AND inbound_order.tenant_id=#{tenantId} AND inbound_order.deleted=0
@@ -34,6 +34,8 @@ public class LoadingCompletedOutboxRepository {
.eq(InboundOutboxEntity::getId, id)
.in(InboundOutboxEntity::getEventType, CROSS_REGION_EVENT_TYPES)
.and(status -> status.eq(InboundOutboxEntity::getPublishStatus, "PENDING")
.and(retry -> retry.isNull(InboundOutboxEntity::getNextRetryAt)
.or().le(InboundOutboxEntity::getNextRetryAt, now))
.or(stale -> stale.eq(InboundOutboxEntity::getPublishStatus, "PROCESSING")
.le(InboundOutboxEntity::getLockedAt, expiredLeaseBefore)))
.set(InboundOutboxEntity::getPublishStatus, "PROCESSING").set(InboundOutboxEntity::getLockedBy, worker)
@@ -0,0 +1,38 @@
package com.baiyue.firstleg.repository;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baiyue.firstleg.entity.InboundOutboxEntity;
import com.baiyue.firstleg.mapper.InboundOutboxMapper;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.apache.ibatis.builder.MapperBuilderAssistant;
import org.springframework.test.util.ReflectionTestUtils;
import java.time.LocalDateTime;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class LoadingCompletedOutboxRepositoryTest {
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
void claimRechecksRetryWindowAfterCandidateSelection() {
TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), "test"),
InboundOutboxEntity.class);
InboundOutboxMapper mapper = mock(InboundOutboxMapper.class);
LoadingCompletedOutboxRepository repository = new LoadingCompletedOutboxRepository();
ReflectionTestUtils.setField(repository, "mapper", mapper);
ArgumentCaptor<LambdaUpdateWrapper> wrapper = ArgumentCaptor.forClass(LambdaUpdateWrapper.class);
when(mapper.update(isNull(InboundOutboxEntity.class), wrapper.capture())).thenReturn(1);
LocalDateTime now = LocalDateTime.of(2026, 8, 25, 12, 0);
assertThat(repository.claim(1L, "worker", now, now.minusMinutes(1))).isTrue();
assertThat(wrapper.getValue().getSqlSegment())
.contains("next_retry_at", "<=", "publish_status", "locked_at");
}
}
@@ -29,6 +29,31 @@ class LoadingPickItemMapperContractTest {
assertThat(progress.getUnresolvedCount()).isEqualTo(1);
}
@Test
void inboundProgressUsesSettledTerminalQuantitiesAndRejectsNonTerminalItems() throws Exception {
Method method = LoadingPickItemMapper.class.getDeclaredMethod(
"selectInboundProgress", long.class, long.class);
String sql = method.getAnnotation(Select.class).value()[0];
assertThat(sql)
.contains("WHEN pick_item.item_status='SKIPPED' AND pick_item.picked_package_count=0 THEN 0")
.contains("WHEN pick_item.item_status='PARTIAL' AND pick_item.picked_package_count>0")
.contains("AND pick_item.picked_package_count<=plan_item.package_count")
.contains("THEN pick_item.picked_package_count")
.contains("ELSE plan_item.package_count")
.contains("WHEN pick_item.id IS NULL THEN 1")
.contains("WHEN pick_item.item_status='PICKED'")
.contains("AND pick_item.picked_package_count=plan_item.package_count THEN 0")
.contains("ELSE 1")
.doesNotContain("pick_item.picked_package_count<>plan_item.package_count");
assertThat(org.springframework.util.StringUtils.countOccurrencesOf(sql,
"pick_item.item_status='PARTIAL'")).isEqualTo(2);
assertThat(org.springframework.util.StringUtils.countOccurrencesOf(sql,
"pick_item.item_status='SKIPPED'")).isEqualTo(2);
assertThat(sql).doesNotContain("pick_item.item_status='PENDING' THEN 0")
.doesNotContain("pick_item.item_status='PICKING' THEN 0");
}
@Test
void completionCheckCoversAllActivePlanItems() throws Exception {
Method method = LoadingPickItemMapper.class.getDeclaredMethod("unfinishedCount", long.class);