Explorar el Código

feat(闪送): 骑手任务列表卡片返回v2多站点逐站地址

- 列表卡片新增 stops 逐站列表(用户顺序):站点类型/序号/文字地址/状态,驱动 ①②③ 序号与逐站进度展示
- 卡片与详情同口径脱敏:不返回联系人、电话、坐标、凭证与收货码
- 列表页按订单 ID 一次 IN 批量取站点避免 N+1;v1 单点卡不附带 stops
qmj hace 4 días
padre
commit
e012f18b4d

+ 2 - 0
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/dto/FlashDeliveryRiderOrderListView.java

@@ -77,4 +77,6 @@ public class FlashDeliveryRiderOrderListView {
     private Boolean canUserCancel;
     /** 用户备注;仅骑手本人任务返回,待抢单卡片为空。 */
     private String userNote;
+    /** v2 多站点单的逐站列表(按用户顺序);卡片只含文字地址与状态,联系人、电话、坐标、凭证见详情。 */
+    private java.util.List<FlashDeliveryStopView> stops;
 }

+ 20 - 2
ruoyi-admin/src/main/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationService.java

@@ -639,9 +639,11 @@ public class FlashDeliveryApplicationService {
         QueryWrapper<FlashDeliveryOrder> query = riderOrdersQuery(riderId, riderVehicleType, selectedTab,
                 effectiveLongitude, effectiveLatitude, true, acceptedPayTypes);
         IPage<FlashDeliveryOrder> orders = orderMapper.selectPage(page(pageNum, pageSize), query);
+        Map<Long, List<FlashDeliveryOrderStop>> stopRows = stopRowsFor(orders.getRecords());
         FlashDeliveryRiderOrderPageView result = new FlashDeliveryRiderOrderPageView();
         result.setRecords(orders.getRecords().stream()
-                .map(order -> riderOrderListView(riderId, order, effectiveLongitude, effectiveLatitude)).toList());
+                .map(order -> riderOrderListView(riderId, order, effectiveLongitude, effectiveLatitude,
+                        stopRows.get(order.getId()))).toList());
         result.setTotal(orders.getTotal());
         result.setCurrent(orders.getCurrent());
         result.setSize(orders.getSize());
@@ -1598,8 +1600,20 @@ public class FlashDeliveryApplicationService {
     }
 
     /** 订单实体转骑手端任务卡片,可附带骑手到取件点的直线距离。 */
+    /** 列表页一次 IN 批量取回 v2 多站点单的站点,避免逐单 N+1;单点单卡片不需要逐站展示。 */
+    private Map<Long, List<FlashDeliveryOrderStop>> stopRowsFor(List<FlashDeliveryOrder> records) {
+        List<Long> orderIds = records.stream()
+                .filter(order -> order.getDeliveryStopCount() != null)
+                .map(FlashDeliveryOrder::getId).toList();
+        if (orderIds.isEmpty()) return Map.of();
+        return stopMapper.selectList(new QueryWrapper<FlashDeliveryOrderStop>()
+                        .in("order_id", orderIds).orderByAsc("stop_order", "id")).stream()
+                .collect(java.util.stream.Collectors.groupingBy(FlashDeliveryOrderStop::getOrderId));
+    }
+
     private FlashDeliveryRiderOrderListView riderOrderListView(Long riderId, FlashDeliveryOrder order,
-                                                                 BigDecimal longitude, BigDecimal latitude) {
+                                                                 BigDecimal longitude, BigDecimal latitude,
+                                                                 List<FlashDeliveryOrderStop> stopRows) {
         FlashDeliveryRiderOrderListView view = new FlashDeliveryRiderOrderListView();
         view.setId(order.getId());
         view.setOrderNo(order.getOrderNo());
@@ -1639,6 +1653,10 @@ public class FlashDeliveryApplicationService {
         view.setCreateTime(order.getCreateTime());
         // 备注仅对中单骑手本人可见;newTask 待抢卡片保持脱敏为空。
         view.setUserNote(Objects.equals(riderId, order.getRiderId()) ? order.getUserNote() : null);
+        // v2 多站点单卡片逐站展示取送地址与状态;与详情同口径脱敏联系人、电话、坐标和凭证。
+        if (order.getDeliveryStopCount() != null && stopRows != null && !stopRows.isEmpty()) {
+            view.setStops(stopViews(stopRows, false, List.of()));
+        }
         return view;
     }
 

+ 3 - 0
ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryApplicationServiceTest.java

@@ -383,6 +383,9 @@ class FlashDeliveryApplicationServiceTest {
         assertTrue(result.getRecords().get(0).getPinRequired());
         // 待抢单卡片继续脱敏用户备注,接单前骑手不可见。
         assertNull(result.getRecords().get(0).getUserNote());
+        // v1 单点卡不附带逐站列表,也不触发站点批量查询。
+        assertNull(result.getRecords().get(0).getStops());
+        verify(fixture.stopMapper, never()).selectList(any());
         ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.query.QueryWrapper> query =
                 ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class);
         verify(fixture.orderMapper).selectPage(any(com.baomidou.mybatisplus.core.metadata.IPage.class), query.capture());

+ 37 - 0
ruoyi-admin/src/test/java/com/ruoyi/app/flashdelivery/service/FlashDeliveryV2Test.java

@@ -186,6 +186,43 @@ class FlashDeliveryV2Test {
         verify(f.billing, never()).insert(any(com.ruoyi.system.domain.UserBilling.class));
     }
 
+    @Test void riderListCardsCarryPerStopAddressesForMultiStopOrders() {
+        Fixture f = new Fixture(); pickup(f, 11L); pickup(f, 12L);
+        var page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<FlashDeliveryOrder>(1, 10);
+        page.setRecords(List.of(f.order));
+        when(f.orders.selectPage(any(), any())).thenReturn(page);
+        var card = f.service.riderOrders(8L, 1, 10, "toPickup", null, null).getRecords().get(0);
+        var stops = card.getStops();
+        // 逐站列表按用户顺序返回,取送类型与状态驱动卡片上的 ①②③ 序号和逐站进度。
+        assertEquals(List.of("PICKUP", "PICKUP", "DELIVERY", "DELIVERY"),
+                stops.stream().map(FlashDeliveryStopView::getStopType).toList());
+        assertEquals(List.of(0, 1, 2, 3), stops.stream().map(FlashDeliveryStopView::getStopOrder).toList());
+        assertEquals(List.of("address 0", "address 1", "address 2", "address 3"),
+                stops.stream().map(FlashDeliveryStopView::getAddress).toList());
+        assertEquals(List.of("PICKED_UP", "PICKED_UP", "PENDING", "PENDING"),
+                stops.stream().map(FlashDeliveryStopView::getStatus).toList());
+        // 卡片与详情同口径脱敏:文字地址可见,联系人、电话、坐标与凭证不落卡片。
+        assertNull(stops.get(0).getName()); assertNull(stops.get(0).getPhone());
+        assertNull(stops.get(0).getLongitude()); assertNull(stops.get(0).getLatitude());
+        assertTrue(stops.get(0).getImageUrls().isEmpty());
+    }
+
+    @Test void newTaskGrabCardsExposeStopAddressesOnly() {
+        Fixture f = new Fixture();
+        f.order.setStatus("WAITING_ACCEPTANCE"); f.order.setRiderId(null);
+        var page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<FlashDeliveryOrder>(1, 10);
+        page.setRecords(List.of(f.order));
+        when(f.orders.selectPage(any(), any())).thenReturn(page);
+        var card = f.service.riderOrders(8L, 1, 10, "newTask", null, null).getRecords().get(0);
+        // 待抢卡片同样给出逐站文字地址供骑手评估路线,但不泄露联系人、电话与坐标。
+        assertEquals(List.of("PICKUP", "PICKUP", "DELIVERY", "DELIVERY"),
+                card.getStops().stream().map(FlashDeliveryStopView::getStopType).toList());
+        assertNull(card.getStops().get(2).getName());
+        assertNull(card.getStops().get(2).getPhone());
+        assertNull(card.getStops().get(2).getLongitude());
+        assertNull(card.getUserNote());
+    }
+
     @Test void riderCanCollectNearbyRemainingPickupBeforeDeliveryWithoutConsent() {
         Fixture f = interleaved(); pickup(f, 11L); pickup(f, 12L);
         assertEquals(13L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());