UnifiedOrderQueryServiceTest.java 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. package com.ruoyi.app.unifiedorder;
  2. import com.baomidou.mybatisplus.core.MybatisConfiguration;
  3. import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  4. import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
  5. import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
  6. import com.ruoyi.app.unifiedorder.dto.UnifiedOrderListItem;
  7. import com.ruoyi.app.unifiedorder.dto.UnifiedOrderPageView;
  8. import com.ruoyi.system.domain.PosOrder;
  9. import com.ruoyi.system.domain.PosStore;
  10. import com.ruoyi.system.domain.flash.FlashDeliveryOrder;
  11. import com.ruoyi.system.domain.flash.FlashDeliveryStatus;
  12. import com.ruoyi.system.mapper.PosOrderMapper;
  13. import com.ruoyi.system.mapper.PosStoreMapper;
  14. import com.ruoyi.system.mapper.flash.FlashDeliveryOrderMapper;
  15. import org.apache.ibatis.builder.MapperBuilderAssistant;
  16. import org.junit.jupiter.api.BeforeAll;
  17. import org.junit.jupiter.api.Test;
  18. import org.mockito.ArgumentCaptor;
  19. import java.util.ArrayList;
  20. import java.util.Date;
  21. import java.util.List;
  22. import java.util.Set;
  23. import static org.junit.jupiter.api.Assertions.assertEquals;
  24. import static org.junit.jupiter.api.Assertions.assertFalse;
  25. import static org.junit.jupiter.api.Assertions.assertNull;
  26. import static org.junit.jupiter.api.Assertions.assertTrue;
  27. import static org.mockito.ArgumentMatchers.any;
  28. import static org.mockito.ArgumentMatchers.anyCollection;
  29. import static org.mockito.Mockito.doReturn;
  30. import static org.mockito.Mockito.mock;
  31. import static org.mockito.Mockito.never;
  32. import static org.mockito.Mockito.times;
  33. import static org.mockito.Mockito.verify;
  34. import static org.mockito.Mockito.when;
  35. /** 统一订单列表服务:两路查询、归并排序、翻页完整性、hasMore、tab 跳过闪送路。 */
  36. class UnifiedOrderQueryServiceTest {
  37. private static final Long USER_ID = 9L;
  38. private final PosOrderMapper posOrderMapper = mock(PosOrderMapper.class);
  39. private final FlashDeliveryOrderMapper flashOrderMapper = mock(FlashDeliveryOrderMapper.class);
  40. private final PosStoreMapper posStoreMapper = mock(PosStoreMapper.class);
  41. private final UnifiedOrderQueryService service =
  42. new UnifiedOrderQueryService(posOrderMapper, flashOrderMapper, posStoreMapper);
  43. @BeforeAll
  44. static void initializeTableMetadata() {
  45. TableInfoHelper.initTableInfo(
  46. new MapperBuilderAssistant(new MybatisConfiguration(), ""), PosOrder.class);
  47. TableInfoHelper.initTableInfo(
  48. new MapperBuilderAssistant(new MybatisConfiguration(), ""), FlashDeliveryOrder.class);
  49. }
  50. @Test
  51. void twoPathsMergeInDisplayOrderAndReturnExactlySize() {
  52. doReturn(pageOf(List.of(
  53. takeaway(21L, 3000L), takeaway(22L, 2000L), takeaway(23L, 1000L))))
  54. .when(posOrderMapper).selectPage(any(), any());
  55. doReturn(pageOf(List.of(
  56. flash(31L, 3000L), flash(32L, 2500L))))
  57. .when(flashOrderMapper).selectPage(any(), any());
  58. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 3);
  59. assertEquals(3, view.getList().size());
  60. // 同秒并列:flash 排在 takeaway 前;整体时间非递增
  61. assertEquals(List.of("flash", "takeaway", "flash"),
  62. view.getList().stream().map(UnifiedOrderListItem::getSourceType).toList());
  63. assertEquals(List.of(31L, 21L, 32L),
  64. view.getList().stream().map(UnifiedOrderListItem::getOrderId).toList());
  65. long previous = Long.MAX_VALUE;
  66. for (UnifiedOrderListItem item : view.getList()) {
  67. assertTrue(item.getCreateTime() <= previous, "createTime 非递增");
  68. previous = item.getCreateTime();
  69. assertEquals("completed", item.getUnifiedStatus());
  70. }
  71. assertTrue(view.isHasMore());
  72. assertEquals("2500000_flash_32", view.getNextCursor());
  73. }
  74. @Test
  75. @SuppressWarnings({"rawtypes", "unchecked"})
  76. void eachPathQueriesSizePlusOneOnFirstPageWithoutCount() {
  77. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  78. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  79. service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 10);
  80. ArgumentCaptor<Page> posPage = ArgumentCaptor.forClass(Page.class);
  81. verify(posOrderMapper).selectPage(posPage.capture(), any());
  82. assertEquals(1L, posPage.getValue().getCurrent());
  83. assertEquals(11L, posPage.getValue().getSize());
  84. assertFalse(posPage.getValue().searchCount());
  85. ArgumentCaptor<Page> flashPage = ArgumentCaptor.forClass(Page.class);
  86. verify(flashOrderMapper).selectPage(flashPage.capture(), any());
  87. assertEquals(11L, flashPage.getValue().getSize());
  88. assertFalse(flashPage.getValue().searchCount());
  89. }
  90. @Test
  91. void scrollingThroughEntireSetYieldsEveryOrderExactlyOnce() {
  92. // 样本全集(含同秒跨源并列、同源同秒 id 决胜)
  93. List<FlashDeliveryOrder> flashAll = List.of(
  94. flash(31L, 3000L), flash(32L, 2000L), flash(34L, 1000L), flash(33L, 1000L));
  95. List<PosOrder> takeawayAll = List.of(
  96. takeaway(21L, 3000L), takeaway(22L, 2000L), takeaway(23L, 1000L));
  97. int size = 3;
  98. List<String> collected = new ArrayList<>();
  99. UnifiedCursor cursor = null;
  100. for (int round = 0; round < 10; round++) {
  101. doReturn(pageOf(afterCursor(flashAll, OrderSource.FLASH, cursor, size + 1)))
  102. .when(flashOrderMapper).selectPage(any(), any());
  103. doReturn(pageOf(afterCursor(takeawayAll, OrderSource.TAKEAWAY, cursor, size + 1)))
  104. .when(posOrderMapper).selectPage(any(), any());
  105. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, null, cursor, size);
  106. view.getList().forEach(item -> collected.add(item.getSourceType() + ":" + item.getOrderId()));
  107. if (!view.isHasMore()) {
  108. assertNull(view.getNextCursor());
  109. break;
  110. }
  111. cursor = UnifiedCursor.parse(view.getNextCursor());
  112. }
  113. // 展示全序:3000F31 3000T21 2000F32 2000T22 1000F34 1000F33 1000T23
  114. assertEquals(List.of("flash:31", "takeaway:21", "flash:32",
  115. "takeaway:22", "flash:34", "flash:33", "takeaway:23"), collected);
  116. }
  117. @Test
  118. @SuppressWarnings({"rawtypes", "unchecked"})
  119. void sizeIsClampedBetweenOneAndFifty() {
  120. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  121. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  122. service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 0);
  123. service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 999);
  124. service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 10);
  125. ArgumentCaptor<Page> pages = ArgumentCaptor.forClass(Page.class);
  126. verify(posOrderMapper, times(3)).selectPage(pages.capture(), any());
  127. assertEquals(List.of(2L, 51L, 11L),
  128. pages.getAllValues().stream().map(Page::getSize).toList());
  129. }
  130. @Test
  131. void singleSourceFilterQueriesOnlyThatMapper() {
  132. doReturn(pageOf(List.of(flash(31L, 3000L)))).when(flashOrderMapper).selectPage(any(), any());
  133. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.FLASH, null, 10);
  134. verify(posOrderMapper, never()).selectPage(any(), any());
  135. verify(flashOrderMapper, times(1)).selectPage(any(), any());
  136. doReturn(pageOf(List.of(takeaway(21L, 3000L)))).when(posOrderMapper).selectPage(any(), any());
  137. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.TAKEAWAY, null, 10);
  138. verify(flashOrderMapper, times(1)).selectPage(any(), any());
  139. verify(posOrderMapper, times(1)).selectPage(any(), any());
  140. }
  141. @Test
  142. @SuppressWarnings({"rawtypes", "unchecked"})
  143. void sourceFilterAddsPosOrderTypePredicate() {
  144. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  145. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  146. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.TAKEAWAY, null, 10);
  147. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.DINEIN, null, 10);
  148. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.PICKUP, null, 10);
  149. service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 10);
  150. ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.Wrapper> captor =
  151. ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.Wrapper.class);
  152. verify(posOrderMapper, times(4)).selectPage(any(), captor.capture());
  153. List<LambdaQueryWrapper<PosOrder>> wrappers = captor.getAllValues().stream()
  154. .map(wrapper -> (LambdaQueryWrapper<PosOrder>) wrapper)
  155. .toList();
  156. // takeaway 仅外送 type=0;dinein=堂食 type=2;pickup=自取 type=1
  157. assertTrue(wrappers.get(0).getSqlSegment().contains("type ="), wrappers.get(0).getSqlSegment());
  158. assertTrue(wrappers.get(0).getParamNameValuePairs().containsValue(0L),
  159. wrappers.get(0).getParamNameValuePairs().toString());
  160. assertTrue(wrappers.get(1).getSqlSegment().contains("type ="), wrappers.get(1).getSqlSegment());
  161. assertTrue(wrappers.get(1).getParamNameValuePairs().containsValue(2L),
  162. wrappers.get(1).getParamNameValuePairs().toString());
  163. assertTrue(wrappers.get(2).getSqlSegment().contains("type ="), wrappers.get(2).getSqlSegment());
  164. assertTrue(wrappers.get(2).getParamNameValuePairs().containsValue(1L),
  165. wrappers.get(2).getParamNameValuePairs().toString());
  166. // source=all 不限 type,堂食/自取单照常混排
  167. assertFalse(wrappers.get(3).getSqlSegment().contains("type ="), wrappers.get(3).getSqlSegment());
  168. }
  169. @Test
  170. void dineinAndPickupQueryOnlyPosOrderPathAndTagCursorWithOwnSource() {
  171. doReturn(pageOf(List.of(takeaway(21L, 3000L), takeaway(22L, 2000L))))
  172. .when(posOrderMapper).selectPage(any(), any());
  173. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.DINEIN, null, 1);
  174. assertEquals(1, view.getList().size());
  175. // 卡片仍是外卖样式,堂食/自取由 type 字段渲染
  176. assertEquals("takeaway", view.getList().get(0).getSourceType());
  177. assertTrue(view.isHasMore());
  178. assertEquals("3000000_dinein_21", view.getNextCursor());
  179. verify(flashOrderMapper, never()).selectPage(any(), any());
  180. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.PICKUP, null, 10);
  181. verify(posOrderMapper, times(2)).selectPage(any(), any());
  182. verify(flashOrderMapper, never()).selectPage(any(), any());
  183. }
  184. @Test
  185. @SuppressWarnings({"rawtypes", "unchecked"})
  186. void dineinCursorDrivesSameSecondIdDecisivePredicateOnNextScroll() {
  187. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  188. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.DINEIN,
  189. UnifiedCursor.parse("3000000_dinein_21"), 10);
  190. ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.Wrapper> captor =
  191. ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.Wrapper.class);
  192. verify(posOrderMapper).selectPage(any(), captor.capture());
  193. LambdaQueryWrapper<PosOrder> wrapper = (LambdaQueryWrapper<PosOrder>) captor.getValue();
  194. // 同源游标:同秒 id 决胜分支存在,保证翻页不丢同秒行
  195. String sql = wrapper.getSqlSegment();
  196. assertTrue(sql.contains(" OR "), sql);
  197. assertTrue(sql.contains("type ="), sql);
  198. }
  199. @Test
  200. void unpaidAndRefundTabsNeverQueryFlashPath() {
  201. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  202. service.query(USER_ID, UnifiedOrderTab.UNPAID, null, null, 10);
  203. service.query(USER_ID, UnifiedOrderTab.REFUND, null, null, 10);
  204. verify(flashOrderMapper, never()).selectPage(any(), any());
  205. verify(posOrderMapper, times(2)).selectPage(any(), any());
  206. }
  207. @Test
  208. void emptyResultReportsNoMoreAndNoCursor() {
  209. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  210. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  211. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, null, null, 10);
  212. assertTrue(view.getList().isEmpty());
  213. assertFalse(view.isHasMore());
  214. assertNull(view.getNextCursor());
  215. }
  216. @Test
  217. void takeawayItemsCarryStoreNameAndAvatarFromSingleBatchLookup() {
  218. PosOrder first = takeaway(21L, 3000L);
  219. first.setMdId(55L);
  220. PosOrder second = takeaway(22L, 2000L);
  221. second.setMdId(56L);
  222. PosOrder third = takeaway(23L, 1000L);
  223. third.setMdId(55L);
  224. doReturn(pageOf(List.of(first, second, third))).when(posOrderMapper).selectPage(any(), any());
  225. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  226. PosStore shopA = new PosStore();
  227. shopA.setId(55);
  228. shopA.setPosName("店铺A");
  229. shopA.setLogo("http://img/a.png");
  230. PosStore shopB = new PosStore();
  231. shopB.setId(56);
  232. shopB.setPosName("店铺B");
  233. shopB.setLogo("http://img/b.png");
  234. when(posStoreMapper.selectBatchIds(anyCollection())).thenReturn(List.of(shopA, shopB));
  235. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.TAKEAWAY, null, 10);
  236. assertEquals(3, view.getList().size());
  237. assertEquals("店铺A", view.getList().get(0).getStoreName());
  238. assertEquals("店铺B", view.getList().get(1).getStoreName());
  239. assertEquals("店铺A", view.getList().get(2).getStoreName());
  240. assertEquals("http://img/a.png", view.getList().get(0).getStoreAvatar());
  241. assertEquals("http://img/b.png", view.getList().get(1).getStoreAvatar());
  242. verify(posStoreMapper, times(1)).selectBatchIds(anyCollection());
  243. }
  244. @Test
  245. void takeawayFallsBackToOrderSnapshotWhenStoreMissing() {
  246. PosOrder order = takeaway(21L, 3000L);
  247. order.setMdId(404L);
  248. order.setPosName("快照店铺");
  249. order.setLogo("http://img/snap.png");
  250. doReturn(pageOf(List.of(order))).when(posOrderMapper).selectPage(any(), any());
  251. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  252. when(posStoreMapper.selectBatchIds(anyCollection())).thenReturn(List.of());
  253. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.TAKEAWAY, null, 10);
  254. assertEquals("快照店铺", view.getList().get(0).getStoreName());
  255. assertEquals("http://img/snap.png", view.getList().get(0).getStoreAvatar());
  256. }
  257. @Test
  258. void takeawayGoodsCountSumsFoodNumberFields() {
  259. PosOrder order = takeaway(21L, 3000L);
  260. order.setFood("[{\"name\":\"餐A\",\"number\":2},{\"name\":\"餐B\",\"number\":3}]");
  261. PosOrder emptyFood = takeaway(22L, 2000L);
  262. PosOrder brokenFood = takeaway(23L, 1000L);
  263. brokenFood.setFood("not-json");
  264. doReturn(pageOf(List.of(order, emptyFood, brokenFood)))
  265. .when(posOrderMapper).selectPage(any(), any());
  266. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  267. when(posStoreMapper.selectBatchIds(anyCollection())).thenReturn(List.of());
  268. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.TAKEAWAY, null, 10);
  269. assertEquals(5, view.getList().get(0).getGoodsCount());
  270. assertNull(view.getList().get(1).getGoodsCount());
  271. assertNull(view.getList().get(2).getGoodsCount());
  272. }
  273. @Test
  274. void refundWithFlashSourceQueriesNothingAtAll() {
  275. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.REFUND, OrderSource.FLASH, null, 10);
  276. assertTrue(view.getList().isEmpty());
  277. assertFalse(view.isHasMore());
  278. assertNull(view.getNextCursor());
  279. verify(posOrderMapper, never()).selectPage(any(), any());
  280. verify(flashOrderMapper, never()).selectPage(any(), any());
  281. }
  282. @Test
  283. void activeOrUnpaidWithTakeawaySourceQueriesOnlyTakeawayPath() {
  284. doReturn(pageOf(List.of())).when(posOrderMapper).selectPage(any(), any());
  285. service.query(USER_ID, UnifiedOrderTab.ACTIVE, OrderSource.TAKEAWAY, null, 10);
  286. service.query(USER_ID, UnifiedOrderTab.UNPAID, OrderSource.TAKEAWAY, null, 10);
  287. verify(posOrderMapper, times(2)).selectPage(any(), any());
  288. verify(flashOrderMapper, never()).selectPage(any(), any());
  289. }
  290. @Test
  291. @SuppressWarnings({"rawtypes", "unchecked"})
  292. void flashPathQueriesBothSenderAndReceiverParticipation() {
  293. doReturn(pageOf(List.of())).when(flashOrderMapper).selectPage(any(), any());
  294. service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.FLASH, null, 10);
  295. ArgumentCaptor<com.baomidou.mybatisplus.core.conditions.Wrapper> captor =
  296. ArgumentCaptor.forClass(com.baomidou.mybatisplus.core.conditions.Wrapper.class);
  297. verify(flashOrderMapper).selectPage(any(), captor.capture());
  298. LambdaQueryWrapper<FlashDeliveryOrder> wrapper =
  299. (LambdaQueryWrapper<FlashDeliveryOrder>) captor.getValue();
  300. String sql = wrapper.getSqlSegment();
  301. assertTrue(sql.contains("user_id ="), sql);
  302. assertTrue(sql.contains("receiver_user_id ="), sql);
  303. assertTrue(sql.contains(" OR "), sql);
  304. assertEquals(2, java.util.Collections.frequency(wrapper.getParamNameValuePairs().values(), USER_ID),
  305. wrapper.getParamNameValuePairs().toString());
  306. }
  307. @Test
  308. void receivedFlashOrderCarriesRolePerViewer() {
  309. FlashDeliveryOrder order = flash(31L, 3000L);
  310. doReturn(pageOf(List.of(order))).when(flashOrderMapper).selectPage(any(), any());
  311. // 收件人 B=77 视角
  312. UnifiedOrderPageView receiverView = service.query(77L, UnifiedOrderTab.ALL, OrderSource.FLASH, null, 10);
  313. assertEquals(1, receiverView.getList().size());
  314. assertEquals("receiver", receiverView.getList().get(0).getRole());
  315. // 寄件人 A=9 视角
  316. UnifiedOrderPageView senderView = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.FLASH, null, 10);
  317. assertEquals(1, senderView.getList().size());
  318. assertEquals("sender", senderView.getList().get(0).getRole());
  319. }
  320. @Test
  321. void selfSentFlashOrderKeepsSenderRole() {
  322. FlashDeliveryOrder order = flash(31L, 3000L);
  323. order.setReceiverUserId(USER_ID);
  324. doReturn(pageOf(List.of(order))).when(flashOrderMapper).selectPage(any(), any());
  325. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.FLASH, null, 10);
  326. assertEquals(1, view.getList().size());
  327. assertEquals("sender", view.getList().get(0).getRole());
  328. }
  329. @Test
  330. void flashItemsCarryOrderVersion() {
  331. FlashDeliveryOrder order = flash(31L, 3000L);
  332. order.setVersion(7);
  333. doReturn(pageOf(List.of(order))).when(flashOrderMapper).selectPage(any(), any());
  334. UnifiedOrderPageView view = service.query(USER_ID, UnifiedOrderTab.ALL, OrderSource.FLASH, null, 10);
  335. assertEquals(7, view.getList().get(0).getOrderVersion());
  336. }
  337. // ---------- helpers ----------
  338. private static PosOrder takeaway(long id, long timeSec) {
  339. PosOrder order = new PosOrder();
  340. order.setId(id);
  341. order.setDdId("DD" + id);
  342. order.setUserId(USER_ID);
  343. order.setCretim(new Date(timeSec * 1000));
  344. order.setState(3L);
  345. order.setType(0L);
  346. order.setPayStatus(1L);
  347. order.setPayType("2");
  348. order.setAfterSaleStatus(0L);
  349. order.setAmount(100);
  350. return order;
  351. }
  352. private static FlashDeliveryOrder flash(long id, long timeSec) {
  353. FlashDeliveryOrder order = new FlashDeliveryOrder();
  354. order.setId(id);
  355. order.setOrderNo("FD" + id);
  356. order.setUserId(USER_ID);
  357. order.setCreateTime(new Date(timeSec * 1000));
  358. order.setStatus(FlashDeliveryStatus.COMPLETED);
  359. order.setAmount(500L);
  360. order.setCurrency("VND");
  361. return order;
  362. }
  363. private static <T> Page<T> pageOf(List<T> records) {
  364. Page<T> page = new Page<>(1, Math.max(records.size(), 1), false);
  365. page.setRecords(new ArrayList<>(records));
  366. return page;
  367. }
  368. /** 模拟 keyset:返回该源中展示序严格晚于游标的前 limit 行。 */
  369. private static <T> List<T> afterCursor(List<T> sourceRows, OrderSource source, UnifiedCursor cursor, int limit) {
  370. List<T> result = new ArrayList<>();
  371. for (T row : sourceRows) {
  372. UnifiedCursor key;
  373. if (source == OrderSource.TAKEAWAY) {
  374. PosOrder order = (PosOrder) row;
  375. key = UnifiedCursor.of(order.getCretim().getTime(), source, order.getId());
  376. } else {
  377. FlashDeliveryOrder order = (FlashDeliveryOrder) row;
  378. key = UnifiedCursor.of(order.getCreateTime().getTime(), source, order.getId());
  379. }
  380. if (cursor == null || UnifiedCursor.DISPLAY_ORDER.compare(key, cursor) > 0) {
  381. result.add(row);
  382. if (result.size() >= limit) {
  383. break;
  384. }
  385. }
  386. }
  387. return result;
  388. }
  389. }