|
@@ -82,6 +82,10 @@ class FlashDeliveryV2Test {
|
|
|
wrongOrder.getStops().add(0, stop("DELIVERY", "0911111112", "台北市松山區B街2號", "d1"));
|
|
wrongOrder.getStops().add(0, stop("DELIVERY", "0911111112", "台北市松山區B街2號", "d1"));
|
|
|
assertThrows(ServiceException.class, () -> f.service.createV2(7L, wrongOrder));
|
|
assertThrows(ServiceException.class, () -> f.service.createV2(7L, wrongOrder));
|
|
|
|
|
|
|
|
|
|
+ FlashDeliveryV2CreateRequest wrongEnd = v2Create();
|
|
|
|
|
+ Collections.swap(wrongEnd.getStops(), 1, 3);
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.createV2(7L, wrongEnd));
|
|
|
|
|
+
|
|
|
FlashDeliveryV2CreateRequest tooMany = v2Create();
|
|
FlashDeliveryV2CreateRequest tooMany = v2Create();
|
|
|
for (int i = 0; i < 2; i++) tooMany.getStops().add(stop("DELIVERY", "091111111" + (4 + i), "台北市中山區C街" + i + "號", "d" + i));
|
|
for (int i = 0; i < 2; i++) tooMany.getStops().add(stop("DELIVERY", "091111111" + (4 + i), "台北市中山區C街" + i + "號", "d" + i));
|
|
|
assertThrows(ServiceException.class, () -> f.service.createV2(7L, tooMany));
|
|
assertThrows(ServiceException.class, () -> f.service.createV2(7L, tooMany));
|
|
@@ -116,6 +120,132 @@ class FlashDeliveryV2Test {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Test void interleavedQuoteAndCreatePreserveCustomerRouteAndRemarks() {
|
|
|
|
|
+ Fixture f = new Fixture();
|
|
|
|
|
+ try (MockedStatic<DictUtils> dict = mockStatic(DictUtils.class)) {
|
|
|
|
|
+ multiStopDicts(dict);
|
|
|
|
|
+ FlashDeliveryV2CreateRequest request = v2Create();
|
|
|
|
|
+ Collections.swap(request.getStops(), 1, 2);
|
|
|
|
|
+ FlashDeliveryQuoteView quote = f.service.quoteV2(request);
|
|
|
|
|
+ request.setPricingId(quote.getPricingId()); request.setPricingVersion(quote.getPricingVersion());
|
|
|
|
|
+ request.setQuotedBaseDeliveryFee(quote.getBaseDeliveryFee()); request.setQuotedDistanceFee(quote.getDistanceFee());
|
|
|
|
|
+ request.setQuotedUrgentFee(quote.getUrgentFee()); request.setQuotedAmount(quote.getAmount());
|
|
|
|
|
+ f.service.createV2(7L, request);
|
|
|
|
|
+ var saved = argThatStops(f);
|
|
|
|
|
+ assertEquals(List.of("PICKUP", "DELIVERY", "PICKUP", "DELIVERY"),
|
|
|
|
|
+ saved.stream().map(FlashDeliveryOrderStop::getStopType).toList());
|
|
|
|
|
+ assertEquals(List.of("取月餅", "送管理室", "取牛奶", "送前台"),
|
|
|
|
|
+ saved.stream().map(FlashDeliveryOrderStop::getRemark).toList());
|
|
|
|
|
+ assertEquals(List.of(0, 1, 2, 3), saved.stream().map(FlashDeliveryOrderStop::getStopOrder).toList());
|
|
|
|
|
+ assertEquals(Arrays.asList(null, 31L, null, 32L),
|
|
|
|
|
+ saved.stream().map(FlashDeliveryOrderStop::getReceiverUserId).toList());
|
|
|
|
|
+ var expected = request.getStops().stream().map(s ->
|
|
|
|
|
+ new com.ruoyi.app.flashdelivery.route.GeoPoint(s.getLatitude(), s.getLongitude())).toList();
|
|
|
|
|
+ assertEquals(expected, f.capturedPoints.subList(0, 4));
|
|
|
|
|
+ assertEquals(expected, f.capturedPoints.subList(4, 8));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test void interleavedFulfilmentKeepsBothProgressCountsAndOriginalTabs() {
|
|
|
|
|
+ Fixture f = interleaved();
|
|
|
|
|
+ pickup(f, 11L); pickup(f, 12L);
|
|
|
|
|
+ assertEquals(13L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
|
|
|
+ f.service.deliverAtStop(8L, 10L, 13L, deliveryProof(13L));
|
|
|
|
|
+ FlashDeliveryOrderDetailView detail = f.service.riderDetail(8L, 10L);
|
|
|
|
|
+ assertEquals("ACCEPTED", detail.getStatus());
|
|
|
|
|
+ assertEquals(2, detail.getPickedUpStopCount()); assertEquals(3, detail.getPickupStopCount());
|
|
|
|
|
+ assertEquals(1, detail.getDeliveredStopCount()); assertEquals(2, detail.getDeliveryStopCount());
|
|
|
|
|
+ assertEquals(15L, detail.getCurrentTargetStopId()); assertFalse(detail.getCanUserCancel());
|
|
|
|
|
+ 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);
|
|
|
|
|
+ assertEquals(2, card.getPickedUpStopCount()); assertEquals(3, card.getPickupStopCount());
|
|
|
|
|
+ assertEquals(1, card.getDeliveredStopCount()); assertEquals(2, card.getDeliveryStopCount());
|
|
|
|
|
+ var query = org.mockito.ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.query.QueryWrapper.class);
|
|
|
|
|
+ verify(f.orders).selectPage(any(), query.capture());
|
|
|
|
|
+ assertTrue(query.getValue().getSqlSegment().contains("status"));
|
|
|
|
|
+ assertTrue(query.getValue().getParamNameValuePairs().containsValue("ACCEPTED"));
|
|
|
|
|
+ // 已送站重试不重复存图、通知,也不恢复旧目标。
|
|
|
|
|
+ f.service.deliverAtStop(8L, 10L, 13L, deliveryProof(13L));
|
|
|
|
|
+ assertEquals(15L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
|
|
|
+ pickup(f, 15L);
|
|
|
|
|
+ assertEquals("PICKED_UP", f.order.getStatus());
|
|
|
|
|
+ assertEquals(3, f.order.getPickedUpStopCount()); assertEquals(1, f.order.getDeliveredStopCount());
|
|
|
|
|
+ f.service.riderOrders(8L, 1, 10, "delivering", null, null);
|
|
|
|
|
+ verify(f.orders, times(2)).selectPage(any(), query.capture());
|
|
|
|
|
+ assertTrue(query.getValue().getSqlSegment().contains("status"));
|
|
|
|
|
+ assertTrue(query.getValue().getParamNameValuePairs().containsValue("PICKED_UP"));
|
|
|
|
|
+ f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L));
|
|
|
|
|
+ assertEquals("DELIVERED", f.order.getStatus()); assertEquals(2, f.order.getDeliveredStopCount());
|
|
|
|
|
+ assertNull(f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
|
|
|
+ assertNotNull(f.order.getPickedUpAt()); assertNotNull(f.order.getDeliveredAt());
|
|
|
|
|
+ verify(f.images, times(5)).insert(any(FlashDeliveryOrderImage.class));
|
|
|
|
|
+ verify(f.notifications, times(3)).notifyPickupStop(any(), any(), anyInt(), eq(3));
|
|
|
|
|
+ verify(f.notifications, times(2)).notifyDeliveryStop(any(), any(), anyInt(), eq(2));
|
|
|
|
|
+ verify(f.billing, never()).insert(any(com.ruoyi.system.domain.UserBilling.class));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test void riderCanCollectNearbyRemainingPickupBeforeDeliveryWithoutConsent() {
|
|
|
|
|
+ Fixture f = interleaved(); pickup(f, 11L); pickup(f, 12L);
|
|
|
|
|
+ assertEquals(13L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
|
|
|
+ when(f.adjustments.insert(any(FlashDeliveryTargetAdjustment.class))).thenReturn(1);
|
|
|
|
|
+ FlashDeliveryTargetRequest request = new FlashDeliveryTargetRequest();
|
|
|
|
|
+ request.setOrderVersion(f.order.getVersion()); request.setClientRequestId("nearby-pickup");
|
|
|
|
|
+ assertEquals(15L, f.service.selectTarget(8L, 10L, 15L, request).getCurrentTargetStopId());
|
|
|
|
|
+ assertEquals(2, f.order.getPickedUpStopCount()); assertEquals(0, f.order.getDeliveredStopCount());
|
|
|
|
|
+ pickup(f, 15L);
|
|
|
|
|
+ assertEquals("PICKED_UP", f.order.getStatus());
|
|
|
|
|
+ assertEquals(13L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
|
|
|
+ assertEquals(List.of(0, 1, 2, 3, 4), f.rows.stream().map(FlashDeliveryOrderStop::getStopOrder).toList());
|
|
|
|
|
+ assertEquals(100L, f.order.getAmount());
|
|
|
|
|
+ var adjustment = org.mockito.ArgumentCaptor.forClass(FlashDeliveryTargetAdjustment.class);
|
|
|
|
|
+ verify(f.adjustments).insert(adjustment.capture());
|
|
|
|
|
+ assertFalse(adjustment.getValue().getConsentDeclared());
|
|
|
|
|
+ assertNull(adjustment.getValue().getReason());
|
|
|
|
|
+ when(f.adjustments.selectOne(any())).thenReturn(adjustment.getValue());
|
|
|
|
|
+ assertEquals(13L, f.service.selectTarget(8L, 10L, 15L, request).getCurrentTargetStopId());
|
|
|
|
|
+ verify(f.adjustments, times(1)).insert(any(FlashDeliveryTargetAdjustment.class));
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.selectTarget(8L, 10L, 13L, request));
|
|
|
|
|
+ verify(f.routes, never()).calculate(anyList(), anyInt());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test void interleavedDeliveryCannotStartWithoutPickupOrFinishWithPendingPickup() {
|
|
|
|
|
+ Fixture f = interleaved();
|
|
|
|
|
+ when(f.adjustments.insert(any(FlashDeliveryTargetAdjustment.class))).thenReturn(1);
|
|
|
|
|
+ FlashDeliveryTargetRequest request = new FlashDeliveryTargetRequest();
|
|
|
|
|
+ request.setOrderVersion(0); request.setClientRequestId("no-goods");
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.selectTarget(8L, 10L, 13L, request));
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 13L, deliveryProof(13L)));
|
|
|
|
|
+ pickup(f, 11L); pickup(f, 12L);
|
|
|
|
|
+ f.service.deliverAtStop(8L, 10L, 13L, deliveryProof(13L));
|
|
|
|
|
+ request.setOrderVersion(f.order.getVersion()); request.setClientRequestId("last-dropoff");
|
|
|
|
|
+ f.service.selectTarget(8L, 10L, 14L, request);
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L)));
|
|
|
|
|
+ assertEquals("ACCEPTED", f.order.getStatus()); assertEquals("PENDING", f.rows.get(4).getStatus());
|
|
|
|
|
+ assertNull(f.order.getDeliveredAt());
|
|
|
|
|
+ verify(f.images, times(3)).insert(any(FlashDeliveryOrderImage.class));
|
|
|
|
|
+ verify(f.notifications, times(1)).notifyDeliveryStop(any(), any(), anyInt(), anyInt());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test void interleavedDeliveryStillValidatesRiderTargetAndOwnPinBeforeWriting() {
|
|
|
|
|
+ Fixture f = interleaved(); pickup(f, 11L); pickup(f, 12L);
|
|
|
|
|
+ f.order.setPinRequired(true); f.rows.get(2).setDeliveryPinCode("1111");
|
|
|
|
|
+ f.rows.get(4).setDeliveryPinCode("2222");
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.deliverAtStop(9L, 10L, 13L, deliveryProof(13L)));
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L)));
|
|
|
|
|
+ FlashDeliveryDeliverRequest proof = deliveryProof(13L); proof.setPinCode("2222");
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 13L, proof));
|
|
|
|
|
+ proof.setPinCode("1111"); proof.setImageUrls(List.of());
|
|
|
|
|
+ assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 13L, proof));
|
|
|
|
|
+ assertEquals("PENDING", f.rows.get(2).getStatus());
|
|
|
|
|
+ verify(f.images, times(2)).insert(any(FlashDeliveryOrderImage.class));
|
|
|
|
|
+ proof.setImageUrls(List.of("/profile/d.jpg"));
|
|
|
|
|
+ f.service.deliverAtStop(8L, 10L, 13L, proof);
|
|
|
|
|
+ assertEquals("DELIVERED", f.rows.get(2).getStatus());
|
|
|
|
|
+ assertEquals("ACCEPTED", f.order.getStatus());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
@Test void v2PickupNotifiesSenderForEachCommittedStopWithoutDuplicateStageMessage() {
|
|
@Test void v2PickupNotifiesSenderForEachCommittedStopWithoutDuplicateStageMessage() {
|
|
|
Fixture f = new Fixture();
|
|
Fixture f = new Fixture();
|
|
|
FlashDeliveryProofRequest proof = new FlashDeliveryProofRequest();
|
|
FlashDeliveryProofRequest proof = new FlashDeliveryProofRequest();
|
|
@@ -255,7 +385,7 @@ class FlashDeliveryV2Test {
|
|
|
assertThrows(ServiceException.class, () -> f.service.deliverAtStop(9L, 10L, 14L, deliveryProof(14L)));
|
|
assertThrows(ServiceException.class, () -> f.service.deliverAtStop(9L, 10L, 14L, deliveryProof(14L)));
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Test void deliveryCannotSkipTargetWithoutRecordedSenderConsent() {
|
|
|
|
|
|
|
+ @Test void deliveryCannotSkipCurrentTargetWithoutSelectingIt() {
|
|
|
Fixture f = ready();
|
|
Fixture f = ready();
|
|
|
assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L)));
|
|
assertThrows(ServiceException.class, () -> f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L)));
|
|
|
verify(f.images, never()).insert(any(FlashDeliveryOrderImage.class));
|
|
verify(f.images, never()).insert(any(FlashDeliveryOrderImage.class));
|
|
@@ -263,14 +393,12 @@ class FlashDeliveryV2Test {
|
|
|
assertEquals(14L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
assertEquals(14L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- @Test void senderConsentAllowsDeliveryTargetChangeAndReturnsToRemainingSequence() {
|
|
|
|
|
|
|
+ @Test void riderCanSelectDeliveryTargetWithoutConsentAndReturnsToRemainingSequence() {
|
|
|
Fixture f = ready();
|
|
Fixture f = ready();
|
|
|
when(f.adjustments.insert(any(FlashDeliveryTargetAdjustment.class))).thenReturn(1);
|
|
when(f.adjustments.insert(any(FlashDeliveryTargetAdjustment.class))).thenReturn(1);
|
|
|
FlashDeliveryTargetRequest request = new FlashDeliveryTargetRequest();
|
|
FlashDeliveryTargetRequest request = new FlashDeliveryTargetRequest();
|
|
|
- request.setClientRequestId("delivery-target-1"); request.setOrderVersion(0); request.setReason("sender asked to deliver second first");
|
|
|
|
|
|
|
+ request.setClientRequestId("delivery-target-1"); request.setOrderVersion(0);
|
|
|
request.setConsentDeclared(false);
|
|
request.setConsentDeclared(false);
|
|
|
- assertThrows(ServiceException.class, () -> f.service.selectTarget(8L, 10L, 14L, request));
|
|
|
|
|
- request.setConsentDeclared(true);
|
|
|
|
|
assertEquals(14L, assertDoesNotThrow(() -> f.service.selectTarget(8L, 10L, 14L, request)).getCurrentTargetStopId());
|
|
assertEquals(14L, assertDoesNotThrow(() -> f.service.selectTarget(8L, 10L, 14L, request)).getCurrentTargetStopId());
|
|
|
f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L));
|
|
f.service.deliverAtStop(8L, 10L, 14L, deliveryProof(14L));
|
|
|
assertEquals(13L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
assertEquals(13L, f.service.riderDetail(8L, 10L).getCurrentTargetStopId());
|
|
@@ -308,7 +436,8 @@ class FlashDeliveryV2Test {
|
|
|
Class<?> quoteType = assertDoesNotThrow(() -> Class.forName("com.ruoyi.app.flashdelivery.dto.FlashDeliveryV2AddressChangeRequest"));
|
|
Class<?> quoteType = assertDoesNotThrow(() -> Class.forName("com.ruoyi.app.flashdelivery.dto.FlashDeliveryV2AddressChangeRequest"));
|
|
|
Class<?> confirmType = assertDoesNotThrow(() -> Class.forName("com.ruoyi.app.flashdelivery.dto.FlashDeliveryV2AddressConfirmRequest"));
|
|
Class<?> confirmType = assertDoesNotThrow(() -> Class.forName("com.ruoyi.app.flashdelivery.dto.FlashDeliveryV2AddressConfirmRequest"));
|
|
|
var body = json.createObjectNode(); body.put("orderId", 10); body.put("orderVersion", 0);
|
|
var body = json.createObjectNode(); body.put("orderId", 10); body.put("orderVersion", 0);
|
|
|
- body.set("stops", json.valueToTree(v2Create().getStops()));
|
|
|
|
|
|
|
+ var interleavedStops = v2Create().getStops(); Collections.swap(interleavedStops, 1, 2);
|
|
|
|
|
+ body.set("stops", json.valueToTree(interleavedStops));
|
|
|
Object quoteRequest = json.treeToValue(body, quoteType);
|
|
Object quoteRequest = json.treeToValue(body, quoteType);
|
|
|
FlashDeliveryQuoteView quote = (FlashDeliveryQuoteView) f.service.getClass().getMethod("quoteAddressV2", Long.class, quoteType).invoke(f.service, 7L, quoteRequest);
|
|
FlashDeliveryQuoteView quote = (FlashDeliveryQuoteView) f.service.getClass().getMethod("quoteAddressV2", Long.class, quoteType).invoke(f.service, 7L, quoteRequest);
|
|
|
assertEquals(1L, quote.getPricingId()); assertEquals(100L, quote.getAmount());
|
|
assertEquals(1L, quote.getPricingId()); assertEquals(100L, quote.getAmount());
|
|
@@ -321,9 +450,9 @@ class FlashDeliveryV2Test {
|
|
|
var detail = (FlashDeliveryOrderDetailView) f.service.getClass().getMethod("updateAddressV2", Long.class, confirmType).invoke(f.service, 7L, confirm);
|
|
var detail = (FlashDeliveryOrderDetailView) f.service.getClass().getMethod("updateAddressV2", Long.class, confirmType).invoke(f.service, 7L, confirm);
|
|
|
assertEquals(4, detail.getStops().size()); assertEquals(2, f.order.getDeliveryStopCount());
|
|
assertEquals(4, detail.getStops().size()); assertEquals(2, f.order.getDeliveryStopCount());
|
|
|
assertEquals("FOOD_INGREDIENTS", f.order.getPackageType()); assertEquals(8, f.order.getQuantity());
|
|
assertEquals("FOOD_INGREDIENTS", f.order.getPackageType()); assertEquals(8, f.order.getQuantity());
|
|
|
- assertEquals(List.of("取月餅", "取牛奶", "送管理室", "送前台"), f.rows.stream().map(FlashDeliveryOrderStop::getRemark).toList());
|
|
|
|
|
|
|
+ assertEquals(List.of("取月餅", "送管理室", "取牛奶", "送前台"), f.rows.stream().map(FlashDeliveryOrderStop::getRemark).toList());
|
|
|
assertEquals(32L, f.rows.get(3).getReceiverUserId());
|
|
assertEquals(32L, f.rows.get(3).getReceiverUserId());
|
|
|
- assertNotNull(f.rows.get(2).getDeliveryPinCode());
|
|
|
|
|
|
|
+ assertNotNull(f.rows.get(1).getDeliveryPinCode());
|
|
|
assertEquals(1, f.order.getVersion());
|
|
assertEquals(1, f.order.getVersion());
|
|
|
verify(f.prices, never()).selectAtTime(anyString(), anyInt());
|
|
verify(f.prices, never()).selectAtTime(anyString(), anyInt());
|
|
|
}
|
|
}
|
|
@@ -334,6 +463,17 @@ class FlashDeliveryV2Test {
|
|
|
f.rows.stream().filter(row -> "PICKUP".equals(row.getStopType())).forEach(row -> row.setStatus("PICKED_UP"));
|
|
f.rows.stream().filter(row -> "PICKUP".equals(row.getStopType())).forEach(row -> row.setStatus("PICKED_UP"));
|
|
|
return f;
|
|
return f;
|
|
|
}
|
|
}
|
|
|
|
|
+ private static Fixture interleaved() {
|
|
|
|
|
+ Fixture f = new Fixture(); f.order.setPinRequired(false); f.order.setPickupStopCount(3);
|
|
|
|
|
+ f.rows.get(3).setStopOrder(4);
|
|
|
|
|
+ f.rows.add(3, stop(15L, 3, "PICKUP", "0911111114"));
|
|
|
|
|
+ return f;
|
|
|
|
|
+ }
|
|
|
|
|
+ private static void pickup(Fixture f, Long stopId) {
|
|
|
|
|
+ FlashDeliveryProofRequest proof = new FlashDeliveryProofRequest();
|
|
|
|
|
+ proof.setImageUrls(List.of("/profile/p.jpg"));
|
|
|
|
|
+ f.service.pickupAtStop(8L, 10L, stopId, proof);
|
|
|
|
|
+ }
|
|
|
private static void receivers(Fixture f) {
|
|
private static void receivers(Fixture f) {
|
|
|
f.order.setReceiverUserId(31L); f.rows.get(2).setReceiverUserId(31L); f.rows.get(3).setReceiverUserId(32L);
|
|
f.order.setReceiverUserId(31L); f.rows.get(2).setReceiverUserId(31L); f.rows.get(3).setReceiverUserId(32L);
|
|
|
}
|
|
}
|