-
-
- [[${book.name}]]
-
+
+
+
-
+
-
-
+
+
+ [[${book.name}]]
+
-
-
+
+
-
-
+
+
+
+
-
+ [[#{author}]]
+
+
+
+
+
+
+
+
+ [[#{author}]]
-
-
-
-
- [[#{view_detail}]]
-
+
+
-
-
+
+
+
-
-
-
-
-
-
-
-
+
+ [[${book.formattedPriceIncludeSymbol}]]
+
+
+
+ [[${book.formattedOriginalPriceIncludeSymbol}]]
+
+
+
+ -[[${book.discountPercent}]]%
+
+
+
+
+
+ [[#{view_detail}]]
+
-
- [[${book.formattedPriceIncludeSymbol}]]
-
-
-
- [[${book.formattedOriginalPriceIncludeSymbol}]]
-
-
-
- -[[${book.discountPercent}]]%
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
+
-
+
+
+
+
+
+
+
+
- [[#{bestselling_books}]]
+
+
+
+
+
-
+
-
+
+ $(function () {
+ var $cd = $('#flashsale-countdown');
+ if (!$cd.length) return;
+
+ var endMs = parseInt($cd.attr('data-end-ms'), 10);
+ if (!endMs || isNaN(endMs)) return;
+
+ function pad2(n) {
+ return String(n).padStart(2, '0');
+ }
+
+ function renderBoxes(h, m, s) {
+ $cd.html(
+ '' + pad2(h) + '' +
+ ':' +
+ '' + pad2(m) + '' +
+ ':' +
+ '' + pad2(s) + ''
+ );
+ }
+
+ function tick() {
+ var diff = endMs - Date.now();
+ if (diff < 0) diff = 0;
+
+ var totalSeconds = Math.floor(diff / 1000);
+ var seconds = totalSeconds % 60;
+ var totalMinutes = Math.floor(totalSeconds / 60);
+ var minutes = totalMinutes % 60;
+ var totalHours = Math.floor(totalMinutes / 60);
+ var hours = totalHours;
+
+ renderBoxes(hours, minutes, seconds);
+
+ if (diff === 0) {
+ clearInterval(timer);
+ }
+ }
+
+ tick();
+ var timer = setInterval(tick, 1000);
+ });
+
+
-
+
\ No newline at end of file
diff --git a/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/controller/service/WebBookFlashSaleControllerService.java b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/controller/service/WebBookFlashSaleControllerService.java
new file mode 100644
index 0000000..b96edc2
--- /dev/null
+++ b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/controller/service/WebBookFlashSaleControllerService.java
@@ -0,0 +1,94 @@
+package org.youngmonkeys.bookstore.web.controller.service;
+
+import com.tvd12.ezyhttp.server.core.annotation.Service;
+import lombok.AllArgsConstructor;
+import org.youngmonkeys.bookstore.web.controller.decorator.WebBookModelDecorator;
+import org.youngmonkeys.bookstore.web.response.WebBookFlashSaleResponse;
+import org.youngmonkeys.bookstore.web.response.WebBookResponse;
+import org.youngmonkeys.bookstore.web.service.WebProductPriceListProductService;
+import org.youngmonkeys.bookstore.web.service.WebProductPriceListService;
+import org.youngmonkeys.ecommerce.entity.ShopProductPriceList;
+import org.youngmonkeys.ecommerce.model.ProductCurrencyModel;
+import org.youngmonkeys.ecommerce.model.ProductModel;
+import org.youngmonkeys.ecommerce.web.service.WebProductService;
+
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Collections;
+import java.util.List;
+
+@Service
+@AllArgsConstructor
+public class WebBookFlashSaleControllerService {
+ private final WebProductService productService;
+ private final WebProductPriceListService productPriceListService;
+ private final WebProductPriceListProductService productPriceListProductService;
+ private final WebBookModelDecorator bookModelDecorator;
+
+ public WebBookFlashSaleResponse getFlashSale(
+ ProductCurrencyModel currency,
+ int limit
+ ) {
+ ShopProductPriceList flashSale =
+ productPriceListService.getActiveFlashSalePriceList();
+ if (flashSale == null) {
+ return null;
+ }
+
+ List books = getFlashSaleBooks(
+ flashSale.getId(),
+ currency,
+ limit
+ );
+
+ return toFlashSaleResponse(
+ flashSale,
+ books
+ );
+ }
+
+ private List getFlashSaleBooks(
+ long priceListId,
+ ProductCurrencyModel currency,
+ int limit
+ ) {
+ List productIds =
+ productPriceListProductService.getProductIdsByPriceListId(
+ priceListId,
+ limit
+ );
+
+ if (productIds.isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List products = productService.getProductsByIds(productIds);
+ return bookModelDecorator.decorateToBookResponses(
+ products,
+ currency
+ );
+ }
+
+ private WebBookFlashSaleResponse toFlashSaleResponse(
+ ShopProductPriceList flashSale,
+ List books
+ ) {
+ return WebBookFlashSaleResponse.builder()
+ .id(flashSale.getId())
+ .displayName(flashSale.getDisplayName())
+ .startApplyAtMs(toEpochMs(flashSale.getStartApplyAt()))
+ .finishApplyAtMs(toEpochMs(flashSale.getFinishApplyAt()))
+ .serverNowMs(System.currentTimeMillis())
+ .books(books)
+ .build();
+ }
+
+ private long toEpochMs(LocalDateTime time) {
+ if (time == null) {
+ return 0L;
+ }
+ return time.atZone(ZoneId.systemDefault())
+ .toInstant()
+ .toEpochMilli();
+ }
+}
diff --git a/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/response/WebBookFlashSaleResponse.java b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/response/WebBookFlashSaleResponse.java
new file mode 100644
index 0000000..f510d8b
--- /dev/null
+++ b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/response/WebBookFlashSaleResponse.java
@@ -0,0 +1,17 @@
+package org.youngmonkeys.bookstore.web.response;
+
+import lombok.Builder;
+import lombok.Getter;
+
+import java.util.List;
+
+@Getter
+@Builder
+public class WebBookFlashSaleResponse {
+ private long id;
+ private String displayName;
+ private long startApplyAtMs;
+ private long finishApplyAtMs;
+ private long serverNowMs;
+ private List books;
+}
diff --git a/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/service/WebProductPriceListProductService.java b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/service/WebProductPriceListProductService.java
new file mode 100644
index 0000000..0474c47
--- /dev/null
+++ b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/service/WebProductPriceListProductService.java
@@ -0,0 +1,31 @@
+package org.youngmonkeys.bookstore.web.service;
+
+import com.tvd12.ezyfox.io.EzyLists;
+import com.tvd12.ezyfox.util.Next;
+import com.tvd12.ezyhttp.server.core.annotation.Service;
+import lombok.AllArgsConstructor;
+import org.youngmonkeys.ecommerce.entity.ShopProductPriceListProduct;
+import org.youngmonkeys.ecommerce.repo.ShopProductPriceListProductRepository;
+
+import java.util.List;
+
+@Service
+@AllArgsConstructor
+public class WebProductPriceListProductService {
+
+ private final ShopProductPriceListProductRepository priceListProductRepository;
+
+ public List getProductIdsByPriceListId(
+ long priceListId,
+ int limit
+ ) {
+ return EzyLists.newArrayList(
+ priceListProductRepository.findByPriceListIdAndIdGtOrderByIdAsc(
+ priceListId,
+ 0L,
+ Next.limit(limit)
+ ),
+ ShopProductPriceListProduct::getProductId
+ );
+ }
+}
diff --git a/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/service/WebProductPriceListService.java b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/service/WebProductPriceListService.java
new file mode 100644
index 0000000..ebe117d
--- /dev/null
+++ b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/service/WebProductPriceListService.java
@@ -0,0 +1,43 @@
+package org.youngmonkeys.bookstore.web.service;
+
+import com.tvd12.ezyfox.util.Next;
+import com.tvd12.ezyhttp.server.core.annotation.Service;
+import lombok.AllArgsConstructor;
+import org.youngmonkeys.ecommerce.entity.ProductPriceListStatus;
+import org.youngmonkeys.ecommerce.entity.ProductPriceListType;
+import org.youngmonkeys.ecommerce.entity.ShopProductPriceList;
+import org.youngmonkeys.ecommerce.repo.ShopProductPriceListRepository;
+import org.youngmonkeys.ezyplatform.time.ClockProxy;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Service
+@AllArgsConstructor
+public class WebProductPriceListService {
+
+ private final ShopProductPriceListRepository shopProductPriceListRepository;
+ private final ClockProxy clock;
+
+ public ShopProductPriceList getActiveFlashSalePriceList() {
+ LocalDateTime now = clock.nowDateTime();
+ int limit = 100;
+ List priceLists =
+ shopProductPriceListRepository
+ .findByTypeAndStatusAndStartApplyAtLteOrderByStartApplyAtAscIdAsc(
+ ProductPriceListType.SCHEDULED_APPLY.toString(),
+ ProductPriceListStatus.APPLIED.toString(),
+ now,
+ Next.limit(limit)
+ );
+
+ for (ShopProductPriceList priceList : priceLists) {
+ LocalDateTime finishApplyAt = priceList.getFinishApplyAt();
+ if (finishApplyAt == null || finishApplyAt.isAfter(now)) {
+ return priceList;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/view/ViewFactory.java b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/view/ViewFactory.java
index 24a4adc..65e2695 100644
--- a/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/view/ViewFactory.java
+++ b/book-store/book-store-web-plugin/src/main/java/org/youngmonkeys/bookstore/web/view/ViewFactory.java
@@ -4,6 +4,7 @@
import com.tvd12.ezyhttp.server.core.view.View;
import lombok.AllArgsConstructor;
import org.youngmonkeys.bookstore.web.controller.service.WebBookControllerService;
+import org.youngmonkeys.bookstore.web.controller.service.WebBookFlashSaleControllerService;
import org.youngmonkeys.ecommerce.model.ProductCurrencyModel;
import org.youngmonkeys.ecommerce.web.service.WebProductCurrencyService;
import org.youngmonkeys.ezyarticle.web.manager.WebPageFragmentManager;
@@ -17,6 +18,7 @@ public class ViewFactory {
private final WebPageFragmentManager pageFragmentManager;
private final WebProductCurrencyService currencyService;
private final WebBookControllerService bookControllerService;
+ private final WebBookFlashSaleControllerService flashSaleControllerService;
public View.Builder newHomeViewBuilder(
long currencyId,
@@ -48,6 +50,12 @@ public View.Builder newHomeViewBuilder(
DEFAULT_BOOKS_LIMIT
)
)
+ .addVariable("flashSale",
+ flashSaleControllerService.getFlashSale(
+ currency,
+ DEFAULT_BOOKS_LIMIT
+ )
+ )
.addVariable(
"fragments",
pageFragmentManager.getPageFragmentMap(
diff --git a/book-store/pom.xml b/book-store/pom.xml
index f056a35..d30b72c 100644
--- a/book-store/pom.xml
+++ b/book-store/pom.xml
@@ -13,7 +13,9 @@
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
--->
+-->
+
4.0.0
org.youngmonkeys
@@ -31,21 +33,21 @@
${env.EZYPLATFORM_HOME}
- 0.9.1
- 1.1.0
- 0.2.2
- 0.1.4
+ 0.9.5
+ 1.1.5
+ 0.2.4
+ 0.1.6
2022.3.1
v2-rev157-1.25.0
1.33.2
1.6.2
- 0.1.5
- 0.3.0
+ 0.1.6
+ 0.3.3
0.1.8
0.3.9
1.18.3
3.5.3
- 0.1.7
+ 0.2.1
0.1.3
0.0.8
@@ -80,26 +82,28 @@
ecommerce-sdk
${ecommerce.version}
system
- ${ezyplatform.home}/web/plugins/ecommerce/lib/ecommerce-sdk-${ecommerce.version}.jar
+ ${ezyplatform.home}/web/plugins/ecommerce/lib/ecommerce-sdk-${ecommerce.version}.jar
+
-
+
com.google.zxing
javase
${zxing.version}
- provided
+ provided
org.jsoup
jsoup
${jsoup.version}
- provided
+ provided
org.youngmonkeys
ezyarticle-sdk
${ezyarticle.version}
system
- ${ezyplatform.home}/admin/plugins/ezyarticle/lib/ezyarticle-sdk-${ezyarticle.version}.jar
+ ${ezyplatform.home}/admin/plugins/ezyarticle/lib/ezyarticle-sdk-${ezyarticle.version}.jar
+
org.youngmonkeys
@@ -109,17 +113,24 @@
${ezyplatform.home}/lib/ezyplatform-common-${ezy.platform.version}.jar
- org.youngmonkeys
- ezyplatform-test-sdk
- ${ezy.platform.version}
- test
-
-
org.youngmonkeys
ezyplatform-socket-sdk
${ezy.platform.version}
provided
+
+ com.tvd12
+ ezyhttp-server-thymeleaf
+ ${ezy.http.version}
+ provided
+
+
+ javax.servlet
+ javax.servlet-api
+ ${javax.servlet.version}
+ true
+ provided
+
[[#{bestselling_books}]]
-
-
-
-
+
+
-
+
+
+
[[#{new_book_s}]]
+
+
+
+
+