From 7b7016e569577e7353085a07036dbd9ec6cf1870 Mon Sep 17 00:00:00 2001 From: xushanchuan Date: Tue, 23 Mar 2021 15:54:04 +0800 Subject: [PATCH] feat: use joda time for cursor process --- .../application/ArticleQueryService.java | 10 ++++- .../application/CommentQueryService.java | 3 +- .../application/CursorPageParameter.java | 14 +++--- .../io/spring/application/CursorPager.java | 8 ++-- .../io/spring/application/DateTimeCursor.java | 23 ++++++++++ src/main/java/io/spring/application/Node.java | 2 +- .../io/spring/application/PageCursor.java | 18 ++++++++ .../spring/application/data/ArticleData.java | 5 ++- .../spring/application/data/CommentData.java | 5 ++- .../graphql/users/ArticleDatafetcher.java | 44 ++++++++++--------- .../graphql/users/CommentDatafetcher.java | 20 +++++---- .../readservice/CommentReadService.java | 3 +- .../resources/mapper/ArticleReadService.xml | 8 ++-- .../resources/mapper/CommentReadService.xml | 4 +- .../article/ArticleQueryServiceTest.java | 10 +++-- 15 files changed, 117 insertions(+), 60 deletions(-) create mode 100644 src/main/java/io/spring/application/DateTimeCursor.java create mode 100644 src/main/java/io/spring/application/PageCursor.java diff --git a/src/main/java/io/spring/application/ArticleQueryService.java b/src/main/java/io/spring/application/ArticleQueryService.java index cb621af..e90643c 100644 --- a/src/main/java/io/spring/application/ArticleQueryService.java +++ b/src/main/java/io/spring/application/ArticleQueryService.java @@ -16,6 +16,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import org.joda.time.DateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -60,7 +61,11 @@ public class ArticleQueryService { } public CursorPager findRecentArticlesWithCursor( - String tag, String author, String favoritedBy, CursorPageParameter page, User currentUser) { + String tag, + String author, + String favoritedBy, + CursorPageParameter page, + User currentUser) { List articleIds = articleReadService.findArticlesWithCursor(tag, author, favoritedBy, page); if (articleIds.size() == 0) { @@ -81,7 +86,8 @@ public class ArticleQueryService { } } - public CursorPager findUserFeedWithCursor(User user, CursorPageParameter page) { + public CursorPager findUserFeedWithCursor( + User user, CursorPageParameter page) { List followdUsers = userRelationshipQueryService.followedUsers(user.getId()); if (followdUsers.size() == 0) { return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); diff --git a/src/main/java/io/spring/application/CommentQueryService.java b/src/main/java/io/spring/application/CommentQueryService.java index ab0d10a..e1906ed 100644 --- a/src/main/java/io/spring/application/CommentQueryService.java +++ b/src/main/java/io/spring/application/CommentQueryService.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import org.joda.time.DateTime; import org.springframework.stereotype.Service; @Service @@ -58,7 +59,7 @@ public class CommentQueryService { } public CursorPager findByArticleIdWithCursor( - String articleId, User user, CursorPageParameter page) { + String articleId, User user, CursorPageParameter page) { List comments = commentReadService.findByArticleIdWithCursor(articleId, page); if (comments.isEmpty()) { return new CursorPager<>(new ArrayList<>(), page.getDirection(), false); diff --git a/src/main/java/io/spring/application/CursorPageParameter.java b/src/main/java/io/spring/application/CursorPageParameter.java index d9145a4..1953137 100644 --- a/src/main/java/io/spring/application/CursorPageParameter.java +++ b/src/main/java/io/spring/application/CursorPageParameter.java @@ -6,13 +6,13 @@ import lombok.NoArgsConstructor; @Data @NoArgsConstructor -public class CursorPageParameter { +public class CursorPageParameter { private static final int MAX_LIMIT = 1000; private int limit = 20; - private String cursor; + private T cursor; private Direction direction; - public CursorPageParameter(String cursor, int limit, Direction direction) { + public CursorPageParameter(T cursor, int limit, Direction direction) { setLimit(limit); setCursor(cursor); setDirection(direction); @@ -26,12 +26,8 @@ public class CursorPageParameter { return limit + 1; } - private void setCursor(String cursor) { - if (cursor == null) { - this.cursor = ""; - } else { - this.cursor = cursor; - } + private void setCursor(T cursor) { + this.cursor = cursor; } private void setLimit(int limit) { diff --git a/src/main/java/io/spring/application/CursorPager.java b/src/main/java/io/spring/application/CursorPager.java index cff42d7..13d55d4 100644 --- a/src/main/java/io/spring/application/CursorPager.java +++ b/src/main/java/io/spring/application/CursorPager.java @@ -29,12 +29,12 @@ public class CursorPager { return previous; } - public String getStartCursor() { - return data.isEmpty() ? "" : data.get(0).getCursor(); + public PageCursor getStartCursor() { + return data.isEmpty() ? null : data.get(0).getCursor(); } - public String getEndCursor() { - return data.isEmpty() ? "" : data.get(data.size() - 1).getCursor(); + public PageCursor getEndCursor() { + return data.isEmpty() ? null : data.get(data.size() - 1).getCursor(); } public enum Direction { diff --git a/src/main/java/io/spring/application/DateTimeCursor.java b/src/main/java/io/spring/application/DateTimeCursor.java new file mode 100644 index 0000000..cfcc86b --- /dev/null +++ b/src/main/java/io/spring/application/DateTimeCursor.java @@ -0,0 +1,23 @@ +package io.spring.application; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; + +public class DateTimeCursor extends PageCursor { + + public DateTimeCursor(DateTime data) { + super(data); + } + + @Override + public String toString() { + return String.valueOf(getData().getMillis()); + } + + public static DateTime parse(String cursor) { + if (cursor == null) { + return null; + } + return new DateTime().withMillis(Long.parseLong(cursor)).withZone(DateTimeZone.UTC); + } +} diff --git a/src/main/java/io/spring/application/Node.java b/src/main/java/io/spring/application/Node.java index 780982e..e4ccac8 100644 --- a/src/main/java/io/spring/application/Node.java +++ b/src/main/java/io/spring/application/Node.java @@ -1,5 +1,5 @@ package io.spring.application; public interface Node { - String getCursor(); + PageCursor getCursor(); } diff --git a/src/main/java/io/spring/application/PageCursor.java b/src/main/java/io/spring/application/PageCursor.java new file mode 100644 index 0000000..0279f3b --- /dev/null +++ b/src/main/java/io/spring/application/PageCursor.java @@ -0,0 +1,18 @@ +package io.spring.application; + +public abstract class PageCursor { + private T data; + + public PageCursor(T data) { + this.data = data; + } + + public T getData() { + return data; + } + + @Override + public String toString() { + return data.toString(); + } +} diff --git a/src/main/java/io/spring/application/data/ArticleData.java b/src/main/java/io/spring/application/data/ArticleData.java index 14ac3d6..3d3c947 100644 --- a/src/main/java/io/spring/application/data/ArticleData.java +++ b/src/main/java/io/spring/application/data/ArticleData.java @@ -1,6 +1,7 @@ package io.spring.application.data; import com.fasterxml.jackson.annotation.JsonProperty; +import io.spring.application.DateTimeCursor; import java.util.List; import lombok.AllArgsConstructor; import lombok.Data; @@ -26,7 +27,7 @@ public class ArticleData implements io.spring.application.Node { private ProfileData profileData; @Override - public String getCursor() { - return String.valueOf(getUpdatedAt().getMillis()); + public DateTimeCursor getCursor() { + return new DateTimeCursor(updatedAt); } } diff --git a/src/main/java/io/spring/application/data/CommentData.java b/src/main/java/io/spring/application/data/CommentData.java index 568649d..1e28d94 100644 --- a/src/main/java/io/spring/application/data/CommentData.java +++ b/src/main/java/io/spring/application/data/CommentData.java @@ -2,6 +2,7 @@ package io.spring.application.data; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import io.spring.application.DateTimeCursor; import io.spring.application.Node; import lombok.AllArgsConstructor; import lombok.Data; @@ -22,7 +23,7 @@ public class CommentData implements Node { private ProfileData profileData; @Override - public String getCursor() { - return String.valueOf(createdAt.getMillis()); + public DateTimeCursor getCursor() { + return new DateTimeCursor(createdAt); } } diff --git a/src/main/java/io/spring/graphql/users/ArticleDatafetcher.java b/src/main/java/io/spring/graphql/users/ArticleDatafetcher.java index 17c0372..a896183 100644 --- a/src/main/java/io/spring/graphql/users/ArticleDatafetcher.java +++ b/src/main/java/io/spring/graphql/users/ArticleDatafetcher.java @@ -8,12 +8,12 @@ import graphql.execution.DataFetcherResult; import graphql.relay.DefaultConnectionCursor; import graphql.relay.DefaultPageInfo; import graphql.schema.DataFetchingEnvironment; -import io.spring.Util; import io.spring.api.exception.ResourceNotFoundException; import io.spring.application.ArticleQueryService; import io.spring.application.CursorPageParameter; import io.spring.application.CursorPager; import io.spring.application.CursorPager.Direction; +import io.spring.application.DateTimeCursor; import io.spring.application.data.ArticleData; import io.spring.application.data.CommentData; import io.spring.core.user.User; @@ -62,11 +62,13 @@ public class ArticleDatafetcher { if (first != null) { articles = articleQueryService.findUserFeedWithCursor( - current, new CursorPageParameter(after, first, Direction.NEXT)); + current, + new CursorPageParameter<>(DateTimeCursor.parse(after), first, Direction.NEXT)); } else { articles = articleQueryService.findUserFeedWithCursor( - current, new CursorPageParameter(before, last, Direction.PREV)); + current, + new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = @@ -77,7 +79,7 @@ public class ArticleDatafetcher { .map( a -> ArticleEdge.newBuilder() - .cursor(a.getCursor()) + .cursor(a.getCursor().toString()) .node(buildArticleResult(a)) .build()) .collect(Collectors.toList())) @@ -110,11 +112,13 @@ public class ArticleDatafetcher { if (first != null) { articles = articleQueryService.findUserFeedWithCursor( - target, new CursorPageParameter(after, first, Direction.NEXT)); + target, + new CursorPageParameter<>(DateTimeCursor.parse(after), first, Direction.NEXT)); } else { articles = articleQueryService.findUserFeedWithCursor( - target, new CursorPageParameter(before, last, Direction.PREV)); + target, + new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); ArticlesConnection articlesConnection = @@ -125,7 +129,7 @@ public class ArticleDatafetcher { .map( a -> ArticleEdge.newBuilder() - .cursor(a.getCursor()) + .cursor(a.getCursor().toString()) .node(buildArticleResult(a)) .build()) .collect(Collectors.toList())) @@ -158,7 +162,7 @@ public class ArticleDatafetcher { null, null, profile.getUsername(), - new CursorPageParameter(after, first, Direction.NEXT), + new CursorPageParameter<>(DateTimeCursor.parse(after), first, Direction.NEXT), current); } else { articles = @@ -166,7 +170,7 @@ public class ArticleDatafetcher { null, null, profile.getUsername(), - new CursorPageParameter(before, last, Direction.PREV), + new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); @@ -179,7 +183,7 @@ public class ArticleDatafetcher { .map( a -> ArticleEdge.newBuilder() - .cursor(a.getCursor()) + .cursor(a.getCursor().toString()) .node(buildArticleResult(a)) .build()) .collect(Collectors.toList())) @@ -212,7 +216,7 @@ public class ArticleDatafetcher { null, profile.getUsername(), null, - new CursorPageParameter(after, first, Direction.NEXT), + new CursorPageParameter<>(DateTimeCursor.parse(after), first, Direction.NEXT), current); } else { articles = @@ -220,7 +224,7 @@ public class ArticleDatafetcher { null, profile.getUsername(), null, - new CursorPageParameter(before, last, Direction.PREV), + new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); @@ -232,7 +236,7 @@ public class ArticleDatafetcher { .map( a -> ArticleEdge.newBuilder() - .cursor(a.getCursor()) + .cursor(a.getCursor().toString()) .node(buildArticleResult(a)) .build()) .collect(Collectors.toList())) @@ -267,7 +271,7 @@ public class ArticleDatafetcher { withTag, authoredBy, favoritedBy, - new CursorPageParameter(after, first, Direction.NEXT), + new CursorPageParameter<>(DateTimeCursor.parse(after), first, Direction.NEXT), current); } else { articles = @@ -275,7 +279,7 @@ public class ArticleDatafetcher { withTag, authoredBy, favoritedBy, - new CursorPageParameter(before, last, Direction.PREV), + new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV), current); } graphql.relay.PageInfo pageInfo = buildArticlePageInfo(articles); @@ -287,7 +291,7 @@ public class ArticleDatafetcher { .map( a -> ArticleEdge.newBuilder() - .cursor(a.getCursor()) + .cursor(a.getCursor().toString()) .node(buildArticleResult(a)) .build()) .collect(Collectors.toList())) @@ -360,12 +364,12 @@ public class ArticleDatafetcher { private DefaultPageInfo buildArticlePageInfo(CursorPager articles) { return new DefaultPageInfo( - Util.isEmpty(articles.getStartCursor()) + articles.getStartCursor() == null ? null - : new DefaultConnectionCursor(articles.getStartCursor()), - Util.isEmpty(articles.getEndCursor()) + : new DefaultConnectionCursor(articles.getStartCursor().toString()), + articles.getEndCursor() == null ? null - : new DefaultConnectionCursor(articles.getEndCursor()), + : new DefaultConnectionCursor(articles.getEndCursor().toString()), articles.hasPrevious(), articles.hasNext()); } diff --git a/src/main/java/io/spring/graphql/users/CommentDatafetcher.java b/src/main/java/io/spring/graphql/users/CommentDatafetcher.java index 8c54f44..602e2fa 100644 --- a/src/main/java/io/spring/graphql/users/CommentDatafetcher.java +++ b/src/main/java/io/spring/graphql/users/CommentDatafetcher.java @@ -7,11 +7,11 @@ import com.netflix.graphql.dgs.InputArgument; import graphql.execution.DataFetcherResult; import graphql.relay.DefaultConnectionCursor; import graphql.relay.DefaultPageInfo; -import io.spring.Util; import io.spring.application.CommentQueryService; import io.spring.application.CursorPageParameter; import io.spring.application.CursorPager; import io.spring.application.CursorPager.Direction; +import io.spring.application.DateTimeCursor; import io.spring.application.data.ArticleData; import io.spring.application.data.CommentData; import io.spring.core.user.User; @@ -72,11 +72,15 @@ public class CommentDatafetcher { if (first != null) { comments = commentQueryService.findByArticleIdWithCursor( - articleData.getId(), current, new CursorPageParameter(after, first, Direction.NEXT)); + articleData.getId(), + current, + new CursorPageParameter<>(DateTimeCursor.parse(after), first, Direction.NEXT)); } else { comments = commentQueryService.findByArticleIdWithCursor( - articleData.getId(), current, new CursorPageParameter(before, last, Direction.PREV)); + articleData.getId(), + current, + new CursorPageParameter<>(DateTimeCursor.parse(before), last, Direction.PREV)); } graphql.relay.PageInfo pageInfo = buildCommentPageInfo(comments); CommentsConnection result = @@ -87,7 +91,7 @@ public class CommentDatafetcher { .map( a -> CommentEdge.newBuilder() - .cursor(a.getCursor()) + .cursor(a.getCursor().toString()) .node(buildCommentResult(a)) .build()) .collect(Collectors.toList())) @@ -101,12 +105,12 @@ public class CommentDatafetcher { private DefaultPageInfo buildCommentPageInfo(CursorPager comments) { return new DefaultPageInfo( - Util.isEmpty(comments.getStartCursor()) + comments.getStartCursor() == null ? null - : new DefaultConnectionCursor(comments.getStartCursor()), - Util.isEmpty(comments.getEndCursor()) + : new DefaultConnectionCursor(comments.getStartCursor().toString()), + comments.getEndCursor() == null ? null - : new DefaultConnectionCursor(comments.getEndCursor()), + : new DefaultConnectionCursor(comments.getEndCursor().toString()), comments.hasPrevious(), comments.hasNext()); } diff --git a/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java b/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java index aaeb547..1f7f1c1 100644 --- a/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java +++ b/src/main/java/io/spring/infrastructure/mybatis/readservice/CommentReadService.java @@ -5,6 +5,7 @@ import io.spring.application.data.CommentData; import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; +import org.joda.time.DateTime; @Mapper public interface CommentReadService { @@ -13,5 +14,5 @@ public interface CommentReadService { List findByArticleId(@Param("articleId") String articleId); List findByArticleIdWithCursor( - @Param("articleId") String articleId, @Param("page") CursorPageParameter page); + @Param("articleId") String articleId, @Param("page") CursorPageParameter page); } diff --git a/src/main/resources/mapper/ArticleReadService.xml b/src/main/resources/mapper/ArticleReadService.xml index 7fee66d..f0b84ce 100644 --- a/src/main/resources/mapper/ArticleReadService.xml +++ b/src/main/resources/mapper/ArticleReadService.xml @@ -116,10 +116,10 @@ AND AFU.username = #{favoritedBy} - + AND A.created_at < #{page.cursor} - + AND A.created_at > #{page.cursor} @@ -138,10 +138,10 @@ #{id} - + AND A.created_at < #{page.cursor} - + AND A.created_at > #{page.cursor} diff --git a/src/main/resources/mapper/CommentReadService.xml b/src/main/resources/mapper/CommentReadService.xml index e8d623d..0ae3606 100644 --- a/src/main/resources/mapper/CommentReadService.xml +++ b/src/main/resources/mapper/CommentReadService.xml @@ -25,10 +25,10 @@ C.article_id = #{articleId} - + AND C.created_at < #{page.cursor} - + AND C.created_at > #{page.cursor} diff --git a/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java b/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java index d870d8e..7c5d285 100644 --- a/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java +++ b/src/test/java/io/spring/application/article/ArticleQueryServiceTest.java @@ -9,6 +9,7 @@ import io.spring.application.ArticleQueryService; import io.spring.application.CursorPageParameter; import io.spring.application.CursorPager; import io.spring.application.CursorPager.Direction; +import io.spring.application.DateTimeCursor; import io.spring.application.Page; import io.spring.application.data.ArticleData; import io.spring.application.data.ArticleDataList; @@ -124,7 +125,7 @@ public class ArticleQueryServiceTest extends DbTestBase { CursorPager recentArticles = queryService.findRecentArticlesWithCursor( - null, null, null, new CursorPageParameter("", 20, Direction.NEXT), user); + null, null, null, new CursorPageParameter<>(null, 20, Direction.NEXT), user); assertEquals(recentArticles.getData().size(), 2); assertEquals(recentArticles.getData().get(0).getId(), article.getId()); @@ -133,14 +134,15 @@ public class ArticleQueryServiceTest extends DbTestBase { null, null, null, - new CursorPageParameter(recentArticles.getEndCursor(), 20, Direction.NEXT), + new CursorPageParameter( + DateTimeCursor.parse(recentArticles.getEndCursor().toString()), 20, Direction.NEXT), user); assertEquals(nodata.getData().size(), 0); - assertEquals(nodata.getStartCursor(), ""); + assertEquals(nodata.getStartCursor(), null); CursorPager prevArticles = queryService.findRecentArticlesWithCursor( - null, null, null, new CursorPageParameter("", 20, Direction.PREV), user); + null, null, null, new CursorPageParameter<>(null, 20, Direction.PREV), user); assertEquals(prevArticles.getData().size(), 2); }