├── .mvn └── wrapper │ ├── maven-wrapper.jar │ ├── maven-wrapper.properties │ └── MavenWrapperDownloader.java ├── src ├── main │ ├── java │ │ └── com │ │ │ └── sellde │ │ │ └── reward │ │ │ ├── enums │ │ │ ├── GiftType.java │ │ │ ├── SelectOption.java │ │ │ ├── UserType.java │ │ │ ├── StatusType.java │ │ │ ├── PaymentOption.java │ │ │ ├── ContactusStatus.java │ │ │ ├── OrderStatus.java │ │ │ ├── DeliveryStatus.java │ │ │ ├── CartAction.java │ │ │ ├── SalesViewOption.java │ │ │ ├── CartStatus.java │ │ │ ├── StaffBadge.java │ │ │ ├── ShippingMethod.java │ │ │ └── CustomerMonthlyBadge.java │ │ │ ├── service │ │ │ ├── model │ │ │ │ ├── ContactusModel.java │ │ │ │ ├── DiscountCouponModel.java │ │ │ │ ├── LuckyDrawContestModel.java │ │ │ │ ├── SettingModel.java │ │ │ │ ├── CouponTrackerModel.java │ │ │ │ ├── MonthlyBadgeModel.java │ │ │ │ ├── ShippingFeePromoModel.java │ │ │ │ ├── GoodsSearchModel.java │ │ │ │ ├── SignupQnAModel.java │ │ │ │ ├── MonthlyBadgeBrandConfigModel.java │ │ │ │ ├── StorePromoModel.java │ │ │ │ ├── CustomerBadgeReport.java │ │ │ │ ├── SignupRewardModel.java │ │ │ │ ├── UserModel.java │ │ │ │ ├── CustomerBadgeModel.java │ │ │ │ ├── SupplierSalesModel.java │ │ │ │ └── CustomerMonthlyBadgeModel.java │ │ │ ├── mapper │ │ │ │ ├── SettingMapper.java │ │ │ │ ├── ContactusMapper.java │ │ │ │ ├── MonthlyBadgeMapper.java │ │ │ │ ├── GoodsSearchMapper.java │ │ │ │ ├── LuckyDrawContestMapper.java │ │ │ │ ├── ShippingPromoMapper.java │ │ │ │ ├── MonthlyBadgeConfigMapper.java │ │ │ │ ├── CustomerBadgeMapper.java │ │ │ │ ├── DiscountCouponMapper.java │ │ │ │ ├── SignupRewardMapper.java │ │ │ │ ├── CouponTrackerMapper.java │ │ │ │ ├── EntityMapper.java │ │ │ │ ├── SignupQnAMapper.java │ │ │ │ ├── UserProfileCopyMapper.java │ │ │ │ └── SupplierSalesMapper.java │ │ │ ├── ContactusService.java │ │ │ ├── LuckyDrawContestService.java │ │ │ ├── userproduct │ │ │ │ └── UserProductHttpClient.java │ │ │ ├── ShippingPromoService.java │ │ │ ├── GoodsSearchCountService.java │ │ │ ├── GoodsSearchService.java │ │ │ ├── biz │ │ │ │ ├── GoodsSearchBizService.java │ │ │ │ ├── SupplierSalesBizService.java │ │ │ │ ├── StorePromoBizService.java │ │ │ │ └── CustomerBadgeReportService.java │ │ │ ├── MonthlyBadgeBrandConfigService.java │ │ │ ├── SupplierSalesService.java │ │ │ ├── CouponTrackerService.java │ │ │ ├── DiscountCouponService.java │ │ │ ├── UserProfileCopyService.java │ │ │ ├── MonthlyBadgeService.java │ │ │ ├── SignupRewardService.java │ │ │ ├── SettingService.java │ │ │ └── SignupQnAService.java │ │ │ ├── util │ │ │ ├── ApiPath.java │ │ │ ├── DateTime.java │ │ │ ├── SwaggerConstant.java │ │ │ ├── Constant.java │ │ │ └── Utils.java │ │ │ ├── repository │ │ │ ├── ContactusRepository.java │ │ │ ├── UserProfileCopyRepository.java │ │ │ ├── ShippingPromoRepository.java │ │ │ ├── SignupQnARepository.java │ │ │ ├── MonthlyBadgeBrandConfigRepository.java │ │ │ ├── SettingRepository.java │ │ │ ├── GoodsSearchCountRepository.java │ │ │ ├── DiscountCouponRepository.java │ │ │ ├── LuckyDrawContestRepository.java │ │ │ ├── SignupRewardRepository.java │ │ │ ├── CouponTrackerRepository.java │ │ │ ├── GoodsSearchRepository.java │ │ │ ├── CustomerBadgeRepository.java │ │ │ ├── MonthlyBadgeRepository.java │ │ │ ├── SupplierSalesRepository.java │ │ │ └── BasketSalesCustomRepository.java │ │ │ ├── domain │ │ │ ├── GoodsSearchCount.java │ │ │ ├── Setting.java │ │ │ ├── LuckyDrawContest.java │ │ │ ├── MonthlyBadge.java │ │ │ ├── Contactus.java │ │ │ ├── ShippingFeePromo.java │ │ │ ├── UserProfileCopy.java │ │ │ ├── AuditDomain.java │ │ │ ├── GoodsSearch.java │ │ │ ├── SignupQnA.java │ │ │ ├── MonthlyBadgeBrandConfig.java │ │ │ ├── CouponTracker.java │ │ │ ├── SignupReward.java │ │ │ ├── DiscountCoupon.java │ │ │ ├── CustomerBadge.java │ │ │ └── SupplierSales.java │ │ │ ├── rest │ │ │ ├── open │ │ │ │ ├── ContactusResource.java │ │ │ │ ├── LuckyDrawContestResource.java │ │ │ │ ├── OpenStorePromoResource.java │ │ │ │ ├── OpenGoodsSearchResource.java │ │ │ │ ├── OpenCustomerBadgeResource.java │ │ │ │ ├── OpenSignupQnAResource.java │ │ │ │ └── OpenSupplierSalesResource.java │ │ │ ├── StorePromoResource.java │ │ │ ├── DefaultResource.java │ │ │ ├── CouponTrackerResource.java │ │ │ ├── CustomerBadgeResource.java │ │ │ ├── SignupRewardResource.java │ │ │ ├── MonthlyBadgeResource.java │ │ │ ├── ShippingFeePromoResource.java │ │ │ └── DiscountCouponResource.java │ │ │ ├── config │ │ │ ├── FlywayConfig.java │ │ │ ├── support │ │ │ │ ├── MapToJsonConverter.java │ │ │ │ └── JsonToMapConverter.java │ │ │ ├── SwaggerConfig.java │ │ │ ├── RestWebclient.java │ │ │ ├── R2dbcConfig.java │ │ │ └── CustomWebFluxConfig.java │ │ │ ├── security │ │ │ ├── AuthenticatedUser.java │ │ │ ├── SecurityConfiguration.java │ │ │ └── cognito │ │ │ │ └── AwsCognitoJwtAuthFilter.java │ │ │ └── SupplyRewardEngineApplication.java │ └── resources │ │ ├── db │ │ └── migration │ │ │ ├── m20220518_alter_coupon.sql │ │ │ ├── m20230305_alter_customer_badge.sql │ │ │ ├── m20220730_contactus.sql │ │ │ ├── m20220729_luckdraw.sql │ │ │ ├── m20220510_coupon_tracker.sql │ │ │ ├── m20221024_alter_user_copy.sql │ │ │ ├── m20230412_signup_qna.sql │ │ │ ├── m20220429_signup_reward.sql │ │ │ ├── m20220501_coupon.sql │ │ │ ├── m20230310_search_goods.sql │ │ │ ├── m20221101_alter_user_copy.sql │ │ │ ├── m20210101_baseline.sql │ │ │ ├── m20220928_supplier_sales.sql │ │ │ └── m20221015_monthly_badge_config.sql │ │ └── config │ │ ├── application.yml │ │ ├── application-prod.yml │ │ ├── application-dev.yml │ │ └── application-localdev.yml └── test │ ├── resources │ ├── logback-test.xml │ ├── json │ │ ├── basket │ │ │ ├── order-100-basket.json │ │ │ ├── saved-basket.json │ │ │ ├── order-100-basket2.json │ │ │ └── order-102-basket.json │ │ ├── saved-order-cart.json │ │ └── create-order-cart.json │ └── config │ │ └── application-test.yml │ └── java │ └── com │ └── sellde │ ├── utility │ ├── DomainUtil.java │ ├── TestUtil.java │ └── CalculateBadgeTest.java │ └── inte │ ├── LuckyDrawContestResourceTest.java │ ├── IntegrationTest.java │ └── CustomerBadgeResourceTest.java ├── .gitignore ├── docker ├── postgres.yaml ├── pulsar.yml └── elasticsearch.yml ├── Dockerfile └── sonar-project.properties /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajeshkumarbehura/product-search/main/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/GiftType.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum GiftType { 4 | MONTHLY, 5 | NA 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/SelectOption.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum SelectOption { 4 | FOR_SELF, 5 | FOR_BUYER 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/UserType.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum UserType { 4 | SELLER, 5 | BUYER, 6 | ADMIN 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/StatusType.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum StatusType { 4 | ENABLE, 5 | DISABLE, 6 | DELETED, 7 | NA 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/PaymentOption.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum PaymentOption { 4 | CASH_ON_DELIVERY, 5 | BANK_TRANSFER, 6 | NA 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/ContactusStatus.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum ContactusStatus { 4 | NEW, 5 | RESPONDED, 6 | NA, 7 | DELETED 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/OrderStatus.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum OrderStatus { 4 | PENDING, 5 | ACCEPTED, 6 | COMPLETED, 7 | CANCEL 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/DeliveryStatus.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum DeliveryStatus { 4 | PENDING, 5 | ACCEPTED, 6 | COMPLETED, 7 | CANCEL 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/ContactusModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.domain.Contactus; 4 | 5 | public class ContactusModel extends Contactus { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/DiscountCouponModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.domain.DiscountCoupon; 4 | 5 | public class DiscountCouponModel extends DiscountCoupon { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/LuckyDrawContestModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.domain.LuckyDrawContest; 4 | 5 | public class LuckyDrawContestModel extends LuckyDrawContest { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/SettingModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.domain.Setting; 4 | import lombok.Data; 5 | 6 | @Data 7 | public class SettingModel extends Setting { 8 | } 9 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220518_alter_coupon.sql: -------------------------------------------------------------------------------- 1 | alter table discount_coupon 2 | add description text; 3 | 4 | alter table discount_coupon 5 | add display_no smallint default 0; 6 | 7 | alter table discount_coupon alter column coupon_name type varchar(350); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | target 4 | docker/db/* 5 | docker/db 6 | doccker/db-test 7 | doccker/db-test/** 8 | docker/pgadmin/* 9 | docker/pgadmin 10 | /docker/db-test/ 11 | .DS_Store 12 | **.DS_Store 13 | **/.DS_Store 14 | /.scannerwork/report-task.txt 15 | /.scannerwork/.sonar_lock 16 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/CouponTrackerModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.UUID; 6 | 7 | @Data 8 | public class CouponTrackerModel { 9 | private UUID id; 10 | private UUID customerId; 11 | private String couponName; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/util/ApiPath.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.util; 2 | 3 | public class ApiPath { 4 | public static final String STORE_GET_VIEW_COUNT = "/store/{storeId}/view-count?startDate={startDate}&endDate={endDate}"; 5 | public static final String USER_GET_CUSTOMER = "/v1/open/user-profile/{userId}"; 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/CartAction.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum CartAction { 4 | BUYER_NEW, 5 | SELLER_ACCEPT, 6 | SELLER_SHIPPED, 7 | SELLER_REJECT, 8 | SELLER_CANCEL, 9 | BUYER_RECEIVED, 10 | BUYER_REJECT, 11 | BUYER_CANCEL, 12 | BUYER_PLACED 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/MonthlyBadgeModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.domain.MonthlyBadge; 4 | 5 | import java.util.UUID; 6 | 7 | public class MonthlyBadgeModel extends MonthlyBadge { 8 | 9 | public UUID getMonthlyBadgeId() { 10 | return getId(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/SalesViewOption.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum SalesViewOption { 4 | TODAY, 5 | THIS_WEEK, 6 | LAST_WEEK, 7 | THIS_MONTH, 8 | LAST_MONTH, 9 | LAST_3_MONTHS; 10 | 11 | public static SalesViewOption defaultOption() { 12 | return LAST_WEEK; 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20230305_alter_customer_badge.sql: -------------------------------------------------------------------------------- 1 | alter table basket_sales 2 | ADD pre_customer_badge varchar(25) default 'NA', 3 | ADD post_customer_badge varchar(25) default 'NA'; 4 | 5 | 6 | alter table customer_badge 7 | ADD total_shipping_price numeric(12, 2) default 0, 8 | ADD total_discount_price numeric(12, 2) default 0; 9 | 10 | -------------------------------------------------------------------------------- /docker/postgres.yaml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | productdb-pg: 4 | image: postgres:13.2 5 | container_name: productdb 6 | environment: 7 | - TZ=GMT+8 8 | - POSTGRES_USER=productdev 9 | - POSTGRES_PASSWORD=admin@334 10 | - POSTGRES_DB=productdb 11 | volumes: 12 | - ./db:/var/lib/postgresql/data 13 | ports: 14 | - 5432:5432 -------------------------------------------------------------------------------- /docker/pulsar.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | pulsar: 4 | image: apachepulsar/pulsar:3.1.1 5 | # If you want to expose these ports outside your dev PC, 6 | # remove the "127.0.0.1:" prefix 7 | ports: 8 | - 127.0.0.1:9080:8080 9 | - 127.0.0.1:6650:6650 10 | environment: 11 | PULSAR_MEM: ' -Xms512m -Xmx512m -XX:MaxDirectMemorySize=1g' 12 | command: bin/pulsar standalone 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/java/jre:17-zulu-alpine 2 | 3 | # Refer to Maven build -> finalName 4 | ARG JAR_FILE=target/supply-reward-engine-1.0.0.jar 5 | 6 | # cd /opt/app 7 | WORKDIR /opt/app 8 | 9 | # cp target/spring-boot-web.jar /opt/app/app.jar 10 | COPY ${JAR_FILE} app.jar 11 | 12 | EXPOSE 80 13 | 14 | # java -jar /opt/app/app.jar 15 | ENTRYPOINT ["java","-jar","-Dspring.profiles.active=${ENV}", "app.jar"] -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/ContactusRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.Contactus; 4 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 5 | import org.springframework.stereotype.Repository; 6 | 7 | import java.util.UUID; 8 | 9 | @Repository 10 | public interface ContactusRepository extends R2dbcRepository { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/CartStatus.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum CartStatus { 4 | /* 5 | Pending Accept 6 | → Pending Delivery 7 | → Pending Received 8 | → Completed 9 | → Cancel 10 | */ 11 | PLACED, 12 | PENDING_ACCEPT, 13 | PENDING_DELIVERY, 14 | PENDING_RECEIVED, 15 | READY_PICKUP, 16 | CANCELLED, 17 | COMPLETED, 18 | NA, 19 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/StaffBadge.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum StaffBadge { 4 | 5 | NA_BADGE("NA"), 6 | DEFAULT_BADGE("BRONZE"), 7 | DEFAULT_BADGE_ID("4e30ecd6-d0f9-4115-bf1e-5d690d19fc83"); 8 | 9 | private final String value; 10 | 11 | StaffBadge(String value) { 12 | this.value=value; 13 | } 14 | 15 | public String getValue(){ 16 | return this.value; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/SettingMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.Setting; 4 | import com.sellde.reward.service.model.SettingModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | 9 | @Component 10 | @Mapper(componentModel = "spring", uses = {}) 11 | public interface SettingMapper extends EntityMapper { 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/ContactusMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.Contactus; 4 | import com.sellde.reward.service.model.ContactusModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @Mapper(componentModel = "spring", uses = {}) 10 | public interface ContactusMapper extends EntityMapper { 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/test/resources/logback-test.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | %d{dd-MM-yyyy HH:mm:ss.SSS} %magenta([%thread]) %highlight(%-5level) %logger{36}.%M - %msg%n 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/ShippingMethod.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum ShippingMethod { 4 | SELF_COLLECT, 5 | DELIVERY, 6 | SELF_COLLECT_AND_DELIVERY, 7 | NA; 8 | 9 | public static ShippingMethod getDefault() { 10 | return DELIVERY; 11 | } 12 | 13 | public static ShippingMethod[] getAll() { 14 | return new ShippingMethod[]{DELIVERY, SELF_COLLECT, SELF_COLLECT_AND_DELIVERY}; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/MonthlyBadgeMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.MonthlyBadge; 4 | import com.sellde.reward.service.model.MonthlyBadgeModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @Mapper(componentModel = "spring", uses = {}) 10 | public interface MonthlyBadgeMapper extends EntityMapper { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/ShippingFeePromoModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.UUID; 8 | 9 | @Data 10 | public class ShippingFeePromoModel { 11 | private UUID id; 12 | private BigDecimal startRange; 13 | private BigDecimal endRange; 14 | private BigDecimal shippingFee; 15 | private StatusType status; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220730_contactus.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE contactus 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | phone_no varchar(15) NOT NULL, 5 | customer_id uuid, 6 | query text, 7 | comment text, 8 | status varchar(25), 9 | created_date timestamptz, 10 | last_modified_date timestamptz 11 | ); 12 | 13 | CREATE INDEX idx_contactus_phone_no ON contactus (phone_no); 14 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/GoodsSearchMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.GoodsSearch; 4 | import com.sellde.reward.service.model.GoodsSearchModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @Mapper(componentModel = "spring", uses = {}) 10 | public interface GoodsSearchMapper { 11 | GoodsSearch fromRequest(GoodsSearchModel.Request request); 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/LuckyDrawContestMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.LuckyDrawContest; 4 | import com.sellde.reward.service.model.LuckyDrawContestModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @Mapper(componentModel = "spring", uses = {}) 10 | public interface LuckyDrawContestMapper extends EntityMapper { 11 | } 12 | 13 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/ShippingPromoMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.ShippingFeePromo; 4 | import com.sellde.reward.service.model.ShippingFeePromoModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | 9 | @Component 10 | @Mapper(componentModel = "spring", uses = {}) 11 | public interface ShippingPromoMapper extends EntityMapper { 12 | } 13 | 14 | -------------------------------------------------------------------------------- /src/test/java/com/sellde/utility/DomainUtil.java: -------------------------------------------------------------------------------- 1 | package com.sellde.utility; 2 | 3 | import com.sellde.reward.service.model.SupplierSalesModel; 4 | 5 | public class DomainUtil { 6 | 7 | public static SupplierSalesModel.BasketRequest newBasketRequestModel() { 8 | return TestUtil.readJsonFile("json/basket/order-100-basket.json", SupplierSalesModel.BasketRequest.class); 9 | } 10 | 11 | public static long randomMobileNo() { 12 | return (long) (Math.random() * 1000000000); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/GoodsSearchModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | 6 | import java.util.UUID; 7 | 8 | public class GoodsSearchModel { 9 | 10 | @Data 11 | public static class Request { 12 | private UUID trackOrCustomerId; 13 | private String keyword; 14 | } 15 | 16 | @Data 17 | @AllArgsConstructor 18 | public static class Result { 19 | private UUID id; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/SignupQnAModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import lombok.Data; 4 | 5 | public class SignupQnAModel { 6 | 7 | @Data 8 | public static class RequestResponse { 9 | private long phoneNo; 10 | private int queryNo; 11 | private String answer; 12 | } 13 | 14 | @Data 15 | public static class GetQuery { 16 | private int queryNo; 17 | private String queryEn; 18 | private String queryVn; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/MonthlyBadgeConfigMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.MonthlyBadgeBrandConfig; 4 | import com.sellde.reward.service.model.MonthlyBadgeBrandConfigModel; 5 | import org.mapstruct.Mapper; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @Mapper(componentModel = "spring", uses = {}) 10 | public interface MonthlyBadgeConfigMapper extends EntityMapper { 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/UserProfileCopyRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.UserProfileCopy; 4 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 5 | import org.springframework.stereotype.Repository; 6 | import reactor.core.publisher.Mono; 7 | 8 | import java.util.UUID; 9 | 10 | @Repository 11 | public interface UserProfileCopyRepository extends R2dbcRepository { 12 | Mono findOneByUserId(UUID userId); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220729_luckdraw.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE lucky_draw_contest 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | phone_no varchar(15) NOT NULL, 5 | gift_type varchar(15), 6 | status varchar(15), 7 | created_date timestamptz, 8 | last_modified_date timestamptz 9 | ); 10 | 11 | CREATE INDEX idx_lucky_draw_contest_phone_no ON lucky_draw_contest (phone_no); 12 | CREATE INDEX idx_lucky_draw_contest_created_date ON lucky_draw_contest (created_date); 13 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/MonthlyBadgeBrandConfigModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.domain.MonthlyBadgeBrandConfig; 4 | import com.sellde.reward.enums.StatusType; 5 | 6 | import java.util.UUID; 7 | 8 | public class MonthlyBadgeBrandConfigModel extends MonthlyBadgeBrandConfig { 9 | 10 | public MonthlyBadgeBrandConfigModel() { 11 | setStatus(StatusType.ENABLE); 12 | } 13 | 14 | public UUID getMonthlyBadgeBrandConfigId() { 15 | return getId(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220510_coupon_tracker.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE coupon_tracker 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | customer_id uuid NOT NULL, 5 | discount_coupon_id uuid NOT NULL, 6 | status varchar(15), 7 | created_date timestamptz, 8 | CONSTRAINT fk_coupon_tracker_discount_coupon_id FOREIGN KEY (discount_coupon_id) REFERENCES discount_coupon (id) 9 | ); 10 | 11 | CREATE INDEX idx_coupon_tracker_customer_id_coupon_id 12 | ON coupon_tracker (customer_id, discount_coupon_id); 13 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/ShippingPromoRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.ShippingFeePromo; 4 | import com.sellde.reward.enums.StatusType; 5 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 6 | import org.springframework.stereotype.Repository; 7 | import reactor.core.publisher.Flux; 8 | 9 | import java.util.UUID; 10 | 11 | @Repository 12 | public interface ShippingPromoRepository extends R2dbcRepository { 13 | Flux findAllByStatus(StatusType status); 14 | } 15 | -------------------------------------------------------------------------------- /src/test/resources/json/basket/order-100-basket.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "28880452-6aa2-40f1-895f-4470bcd656db", 3 | "customerId": "4d61f984-667b-418f-865a-818e85a7085c", 4 | "orderNo": 100, 5 | "totalPrice": 4500000, 6 | "totalRelyBulkPrice": 5000000, 7 | "shippingPrice": 15000, 8 | "discountPrice": 40000, 9 | "totalMarginPrice": 22250, 10 | "selectOption": "FOR_SELF", 11 | "basketStatus": "ORDER_CONFIRMED", 12 | "preCustomerBadge": "BRONZE", 13 | "postCustomerBadge": "SILVER", 14 | "orderDate": "2022-09-04T07:40:47.242815Z", 15 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db" 16 | } -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20221024_alter_user_copy.sql: -------------------------------------------------------------------------------- 1 | drop table if exists user_profile_copy; 2 | 3 | CREATE TABLE user_profile_copy 4 | ( 5 | id uuid DEFAULT uuid_generate_v4() primary key, 6 | user_id uuid NOT NULL, 7 | user_name varchar(100), 8 | phone_no int default 0, 9 | user_type varchar(15) not null, 10 | status varchar(15) not null, 11 | created_date timestamptz, 12 | last_modified_date timestamptz 13 | ); 14 | CREATE UNIQUE INDEX idx_user_profile_copy_user_id ON user_profile_copy (user_id); -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/SignupQnARepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.SignupQnA; 4 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 5 | import org.springframework.stereotype.Repository; 6 | import reactor.core.publisher.Flux; 7 | 8 | import java.util.UUID; 9 | 10 | @Repository 11 | public interface SignupQnARepository extends R2dbcRepository { 12 | 13 | Flux findAllByPhoneNoOrderByQueryNo(long phoneNo); 14 | 15 | Flux findAllByPhoneNoAndAnswerIsNotNullOrderByQueryNo(long phoneNo); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/MonthlyBadgeBrandConfigRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.MonthlyBadgeBrandConfig; 4 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 5 | import org.springframework.stereotype.Repository; 6 | import reactor.core.publisher.Flux; 7 | 8 | import java.util.UUID; 9 | 10 | 11 | @Repository 12 | public interface MonthlyBadgeBrandConfigRepository extends R2dbcRepository { 13 | 14 | Flux findAllByMonthlyBadgeIdOrderByGoodsBrandName(UUID monthlyBadgeId); 15 | } 16 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20230412_signup_qna.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE signup_qna 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | phone_no int not null, 5 | query_no smallint default 0, 6 | query_en text default 'NA', 7 | query_vn text default 'NA', 8 | answer varchar(200) default 'NA', 9 | status varchar(15) not null, 10 | created_date timestamptz, 11 | last_modified_date timestamptz 12 | ); 13 | 14 | CREATE UNIQUE INDEX idx_customer_qna_phone_no_query_no ON signup_qna (phone_no, query_no); 15 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/CustomerBadgeMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.CustomerBadge; 4 | import com.sellde.reward.service.model.CustomerBadgeModel; 5 | import com.sellde.reward.service.model.SupplierSalesModel; 6 | import org.mapstruct.Mapper; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @Mapper(componentModel = "spring", uses = {}) 11 | public interface CustomerBadgeMapper extends EntityMapper { 12 | CustomerBadge toEntityFromCustomerBadgeRequest(SupplierSalesModel.CustomerBadgeRequest request); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/SettingRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.Setting; 4 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 5 | import org.springframework.stereotype.Repository; 6 | import reactor.core.publisher.Flux; 7 | import reactor.core.publisher.Mono; 8 | 9 | import java.util.List; 10 | import java.util.UUID; 11 | 12 | @Repository 13 | public interface SettingRepository extends R2dbcRepository { 14 | 15 | Mono findDistinctByConfigName(String configName); 16 | 17 | Flux findAllByConfigNameIsIn(List configNameList); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/DiscountCouponMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.DiscountCoupon; 4 | import com.sellde.reward.domain.SignupReward; 5 | import com.sellde.reward.service.model.DiscountCouponModel; 6 | import com.sellde.reward.service.model.SignupRewardModel; 7 | import com.sellde.reward.service.model.SignupRewardModel.RangeModel; 8 | import org.mapstruct.Mapper; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.List; 12 | 13 | 14 | @Component 15 | @Mapper(componentModel = "spring", uses = {}) 16 | public interface DiscountCouponMapper extends EntityMapper { 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/config/application.yml: -------------------------------------------------------------------------------- 1 | aws: 2 | region: ap-southeast-1 3 | user-pool: ap-southeast-1_U3EdmFdGS 4 | 5 | spring: 6 | application: 7 | name: ProductSearch 8 | 9 | r2dbc: 10 | database: productdb 11 | username: productdev 12 | password: admin@334 13 | url: r2dbc:postgresql://localhost:5432/${spring.r2dbc.database} 14 | 15 | flyway: 16 | user: ${spring.r2dbc.username} 17 | password: ${spring.r2dbc.password} 18 | url: jdbc:postgresql://localhost:5432/${spring.r2dbc.database} 19 | 20 | 21 | server: 22 | port: 8080 23 | 24 | 25 | sellde: 26 | api-user-product: https://titan-dev.sellde.cloud/user-product 27 | supply-reward-api-key: DEV-fc2f9035-42ee-41da-8edf-cd2b4815f5f2 -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/StorePromoModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import lombok.Data; 4 | 5 | import java.util.List; 6 | 7 | @Data 8 | public class StorePromoModel { 9 | private boolean isStoreShippingFeePromoOn; 10 | private boolean isStoreSignupDiscountPromoOn; 11 | private boolean isStoreDiscountCouponPromoOn; 12 | 13 | private String storeShippingFeePromoType; 14 | private String storeSignupBonusPromoType; 15 | private String storeDiscountCouponPromoType; 16 | 17 | private List shippingFeePromoList; 18 | private List signupRewardList; 19 | private List discountCouponList; 20 | } 21 | -------------------------------------------------------------------------------- /src/test/resources/config/application-test.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: OrderEngine 4 | 5 | r2dbc: 6 | url: r2dbc:postgresql://localhost:3542/test-supply-reward-db 7 | username: rewardtestuser 8 | password: admin@6645 9 | 10 | 11 | 12 | flyway: 13 | url: jdbc:postgresql://localhost:3542/test-supply-reward-db 14 | user: ${spring.r2dbc.username} 15 | password: ${spring.r2dbc.password} 16 | 17 | security: 18 | oauth2: 19 | resourceserver: 20 | jwt: 21 | issuer-uri: https://cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_F64JehlME 22 | 23 | server: 24 | port: 7001 25 | 26 | 27 | sellde: 28 | supply-reward-api-key: TEST-fc2f9035-42ee-41da-8edf-cd2b4815f5f1 29 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/GoodsSearchCount.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.relational.core.mapping.Column; 7 | import org.springframework.data.relational.core.mapping.Table; 8 | 9 | import java.util.UUID; 10 | 11 | 12 | @Data 13 | @Table("goods_search_count") 14 | public class GoodsSearchCount { 15 | 16 | @Id 17 | @Column("id") 18 | private UUID id; 19 | 20 | @Column("keyword") 21 | private String keyword; 22 | 23 | @Column("used_count") 24 | private Long usedCount; 25 | 26 | @Column("status") 27 | private StatusType status; 28 | 29 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/SignupRewardMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.SignupReward; 4 | import com.sellde.reward.service.model.SignupRewardModel; 5 | import com.sellde.reward.service.model.SignupRewardModel.RangeModel; 6 | import org.mapstruct.Mapper; 7 | import org.springframework.stereotype.Component; 8 | 9 | import java.util.List; 10 | 11 | 12 | @Component 13 | @Mapper(componentModel = "spring", uses = {}) 14 | public interface SignupRewardMapper extends EntityMapper { 15 | 16 | List toRangeEntityList(List dtoList); 17 | 18 | List toRangeModelList(List entityList); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/CustomerBadgeReport.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import lombok.*; 4 | import lombok.experimental.FieldDefaults; 5 | 6 | import java.util.UUID; 7 | 8 | @Data 9 | @With 10 | @AllArgsConstructor 11 | @NoArgsConstructor 12 | @FieldDefaults(level = AccessLevel.PRIVATE) 13 | public class CustomerBadgeReport { 14 | UUID customerId; 15 | String customerName; 16 | long phoneNo; 17 | int lastMonth; 18 | String lastMonthName; 19 | CustomerBadgeModel lastMonthBadge; 20 | int currentMonth; 21 | String currentMonthName; 22 | CustomerBadgeModel currentMonthBadge; 23 | int nextMonth; 24 | String nextMonthName; 25 | CustomerBadgeModel nextMonthBadge; 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/SignupRewardModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | 6 | import java.math.BigDecimal; 7 | import java.util.List; 8 | import java.util.UUID; 9 | 10 | @Data 11 | public class SignupRewardModel { 12 | private String configValue; 13 | private String configName; 14 | private String rewardType; 15 | private List rangeList; 16 | 17 | @Data 18 | public static class RangeModel { 19 | private UUID id; 20 | private BigDecimal startRange; 21 | private BigDecimal endRange; 22 | private BigDecimal discountPrice; 23 | private StatusType status; 24 | } 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /docker/elasticsearch.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | elasticsearch: 4 | image: docker.elastic.co/elasticsearch/elasticsearch:8.10.4 5 | # volumes: 6 | # - ~/volumes/jhipster/esProject/elasticsearch/:/usr/share/elasticsearch/data/ 7 | # If you want to expose these ports outside your dev PC, 8 | # remove the "127.0.0.1:" prefix 9 | ports: 10 | - 127.0.0.1:9200:9200 11 | - 127.0.0.1:9300:9300 12 | environment: 13 | - 'ES_JAVA_OPTS=-Xms256m -Xmx256m' 14 | - 'discovery.type=single-node' 15 | - 'xpack.security.enabled=false' 16 | healthcheck: 17 | test: ['CMD', 'curl', '-f', 'http://localhost:9200/_cluster/health?wait_for_status=green&timeout=10s'] 18 | interval: 5s 19 | timeout: 10s 20 | retries: 10 21 | -------------------------------------------------------------------------------- /sonar-project.properties: -------------------------------------------------------------------------------- 1 | sonar.projectKey=titan-supply-reward-api 2 | sonar.projectName=titan-supply-reward-api 3 | sonar.projectVersion=1.0 4 | 5 | sonar.sources=src/main/ 6 | sonar.host.url=http://sonar-dev.sellde.cloud 7 | sonar.login=a37690bc9173686c01c51a3a0456b78b1ec5be48 8 | 9 | #sonar.tests=src/test/ 10 | #sonar.coverage.jacoco.xmlReportPaths=target/site/**/jacoco*.xml 11 | #sonar.java.codeCoveragePlugin=jacoco 12 | #sonar.junit.reportPaths=target/surefire-reports,target/failsafe-reports 13 | #sonar.testExecutionReportPaths=target/test-results/jest/TESTS-results-sonar.xml 14 | #sonar.javascript.lcov.reportPaths=target/test-results/lcov.info 15 | 16 | sonar.sourceEncoding=UTF-8 17 | sonar.exclusions=target/ 18 | sonar.language=java 19 | sonar.java.binaries=target/classes 20 | 21 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/ContactusResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | import com.sellde.reward.service.ContactusService; 4 | import com.sellde.reward.service.model.ContactusModel; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.*; 8 | import reactor.core.publisher.Mono; 9 | 10 | @RestController 11 | @RequestMapping("v1/open/contactus") 12 | public class ContactusResource { 13 | 14 | @Autowired 15 | private ContactusService contactusService; 16 | 17 | @PostMapping() 18 | @ResponseStatus(HttpStatus.OK) 19 | public Mono create(@RequestBody ContactusModel model) { 20 | return contactusService.create(model); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/GoodsSearchCountRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.GoodsSearchCount; 4 | import com.sellde.reward.enums.StatusType; 5 | import org.springframework.data.domain.PageRequest; 6 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 7 | import org.springframework.stereotype.Repository; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.util.UUID; 12 | 13 | @Repository 14 | public interface GoodsSearchCountRepository extends R2dbcRepository { 15 | 16 | Mono findOneByKeywordAndStatus(String keyword, StatusType statusType); 17 | 18 | Flux findAllByStatus(StatusType status, PageRequest pageRequest); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/CouponTrackerMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.CouponTracker; 4 | import com.sellde.reward.service.model.CouponTrackerModel; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.Mapping; 7 | import org.mapstruct.MappingTarget; 8 | import org.mapstruct.Named; 9 | import org.springframework.stereotype.Component; 10 | 11 | 12 | @Component 13 | @Mapper(componentModel = "spring", uses = {}) 14 | public interface CouponTrackerMapper extends EntityMapper { 15 | 16 | @Named("toUpdateEntity") 17 | @Mapping(target = "id", ignore = true) 18 | @Mapping(target = "createdDate", ignore = true) 19 | CouponTracker toUpdateEntity(@MappingTarget CouponTracker entity, CouponTrackerModel model); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/DiscountCouponRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.DiscountCoupon; 4 | import com.sellde.reward.enums.StatusType; 5 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 6 | import org.springframework.stereotype.Repository; 7 | import reactor.core.publisher.Flux; 8 | import reactor.core.publisher.Mono; 9 | 10 | import java.util.List; 11 | import java.util.UUID; 12 | 13 | 14 | @Repository 15 | public interface DiscountCouponRepository extends R2dbcRepository { 16 | 17 | Flux findAllByStatus(StatusType status); 18 | 19 | Mono findByCouponName(String couponName); 20 | 21 | Flux findByCouponNameIsIn(List couponNameList,StatusType status); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/LuckyDrawContestRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.LuckyDrawContest; 4 | import org.springframework.data.r2dbc.repository.Query; 5 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 6 | import org.springframework.stereotype.Repository; 7 | import reactor.core.publisher.Mono; 8 | 9 | import java.util.UUID; 10 | 11 | @Repository 12 | public interface LuckyDrawContestRepository extends R2dbcRepository { 13 | 14 | @Query(""" 15 | select * from lucky_draw_contest 16 | where phone_no = :phoneNo 17 | and to_char(created_date,'dd-MM-YYYY') = :createdDateValue 18 | """) 19 | Mono findBYPhoneAndDay(String phoneNo, String createdDateValue); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/SignupRewardRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.SignupReward; 4 | import com.sellde.reward.enums.StatusType; 5 | import org.springframework.data.r2dbc.repository.Query; 6 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 7 | import org.springframework.stereotype.Repository; 8 | import reactor.core.publisher.Flux; 9 | 10 | import java.util.UUID; 11 | 12 | 13 | @Repository 14 | public interface SignupRewardRepository extends R2dbcRepository { 15 | @Query(""" 16 | select * from signup_reward 17 | order by reward_type 18 | """) 19 | Flux findAllOrderByRewardType(); 20 | 21 | Flux findAllByStatusOrderByRewardType(StatusType statusType); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/EntityMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import org.mapstruct.*; 4 | 5 | import java.util.List; 6 | 7 | 8 | public interface EntityMapper { 9 | E toEntity(M model); 10 | 11 | M toModel(E entity); 12 | 13 | List toEntity(List dtoList); 14 | 15 | List toModel(List entityList); 16 | 17 | @Named("partialUpdate") 18 | @BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE) 19 | void partialUpdate(@MappingTarget E entity, M model); 20 | 21 | @Named("toUpdateEntity") 22 | @Mapping(target = "id", ignore = true) 23 | @Mapping(target = "createdDate", ignore = true) 24 | @Mapping(target = "lastModifiedDate", ignore = true) 25 | E toUpdateEntity(@MappingTarget E entity, M model); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/SignupQnAMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.SignupQnA; 4 | import com.sellde.reward.service.model.SignupQnAModel; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.Mapping; 7 | import org.mapstruct.MappingTarget; 8 | import org.mapstruct.Named; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Component 12 | @Mapper(componentModel = "spring", uses = {}) 13 | public interface SignupQnAMapper extends EntityMapper { 14 | 15 | @Named("toUpdateEntity") 16 | @Mapping(target = "id", ignore = true) 17 | SignupQnA toUpdateEntity(@MappingTarget SignupQnA entity, SignupQnAModel.RequestResponse model); 18 | 19 | SignupQnAModel.GetQuery toOnlyQuery(SignupQnA entity); 20 | } 21 | 22 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220429_signup_reward.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE signup_reward 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | reward_type varchar(200) NOT NULL, 5 | config_name varchar(200) default 'NA', 6 | config_value varchar(200) default 'NA', 7 | start_range numeric(12, 2) default 0, 8 | end_range numeric(12, 2) default 0, 9 | discount_price numeric(12, 2) default 0, 10 | status varchar(15), 11 | created_date timestamptz, 12 | last_modified_date timestamptz 13 | ); 14 | 15 | 16 | -- static data 17 | INSERT INTO public.setting (config_type, config_name, config_value, status, created_date, last_modified_date) 18 | VALUES ('SIGNUP_PROMO', 'isStoreSignupDiscountPromoOn', 'true', 'ENABLE', 'now()', 'now()'); 19 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220501_coupon.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE discount_coupon 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | coupon_name varchar(50) NOT NULL unique, 5 | coupon_price numeric(12, 2) default 0, 6 | min_buying_range numeric(12, 2) default 0, 7 | has_fixed_time boolean default false, 8 | customer_count smallint, 9 | start_date timestamptz, 10 | end_date timestamptz, 11 | status varchar(15), 12 | created_date timestamptz, 13 | last_modified_date timestamptz 14 | ); 15 | 16 | 17 | -- static data 18 | INSERT INTO public.setting (config_type, config_name, config_value, status, created_date, last_modified_date) 19 | VALUES ('DISCOUNT_COUPON_PROMO', 'isStoreDiscountCouponPromoOn', 'false', 'ENABLE', 'now()', 'now()'); 20 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/Setting.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @Table("setting") 15 | @EqualsAndHashCode(callSuper = false) 16 | public class Setting extends AuditDomain { 17 | 18 | @Id 19 | @Column("id") 20 | private UUID id; 21 | 22 | @Column("config_type") 23 | private String configType; 24 | 25 | @Column("config_name") 26 | private String configName; 27 | 28 | @Column("config_value") 29 | private String configValue; 30 | 31 | @Column("status") 32 | private StatusType status; 33 | 34 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/LuckyDrawContest.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.GiftType; 4 | import com.sellde.reward.enums.StatusType; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import org.springframework.data.annotation.Id; 8 | import org.springframework.data.relational.core.mapping.Column; 9 | import org.springframework.data.relational.core.mapping.Table; 10 | 11 | import java.util.UUID; 12 | 13 | 14 | @Data 15 | @Table("lucky_draw_contest") 16 | @EqualsAndHashCode(callSuper = false) 17 | public class LuckyDrawContest extends AuditDomain { 18 | 19 | @Id 20 | @Column("id") 21 | private UUID id; 22 | 23 | @Column("phone_no") 24 | private String phoneNo; 25 | 26 | @Column("gift_type") 27 | private GiftType giftType = GiftType.NA; 28 | 29 | @Column("status") 30 | private StatusType status = StatusType.ENABLE; 31 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/UserProfileCopyMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.UserProfileCopy; 4 | import com.sellde.reward.enums.StatusType; 5 | import com.sellde.reward.service.model.UserModel; 6 | import org.mapstruct.Mapper; 7 | import org.mapstruct.Mapping; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @Mapper(componentModel = "spring", uses = {}) 12 | public interface UserProfileCopyMapper { 13 | 14 | @Mapping(target = "id", ignore = true) 15 | @Mapping(target = "userId", source = "id") 16 | @Mapping(target = "userName", source = "name") 17 | @Mapping(target = "phoneNo", source = "mobileNo") 18 | @Mapping(target = "userType", source = "type") 19 | UserProfileCopy toEntity(UserModel.Request model); 20 | 21 | 22 | UserModel.ProfileModel toModel(UserProfileCopy entity); 23 | 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/CouponTrackerRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.CouponTracker; 4 | import com.sellde.reward.enums.StatusType; 5 | import org.springframework.data.r2dbc.repository.Query; 6 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 7 | import org.springframework.stereotype.Repository; 8 | import reactor.core.publisher.Mono; 9 | 10 | import java.util.UUID; 11 | 12 | @Repository 13 | public interface CouponTrackerRepository extends R2dbcRepository { 14 | 15 | @Query(""" 16 | select coalesce (count(id),0) from coupon_tracker 17 | where customer_id = :customerId 18 | and discount_coupon_id = :couponId 19 | and status = :status 20 | """) 21 | Mono countCustomerAppliedCouponWithStatus(UUID couponId, UUID customerId, StatusType status); 22 | 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/test/java/com/sellde/utility/TestUtil.java: -------------------------------------------------------------------------------- 1 | package com.sellde.utility; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; 5 | 6 | import java.io.File; 7 | import java.util.UUID; 8 | 9 | public class TestUtil { 10 | 11 | public static UUID uuid() { 12 | return UUID.randomUUID(); 13 | } 14 | 15 | public static String uuidAsString() { 16 | return UUID.randomUUID().toString(); 17 | } 18 | 19 | public static T readJsonFile(String filename, Class tClass) { 20 | var objectMapper = new ObjectMapper(); 21 | objectMapper.registerModule(new JavaTimeModule()); 22 | try { 23 | return objectMapper.readValue(new File("src/test/resources/"+filename), tClass); 24 | } catch (Exception e) { 25 | e.printStackTrace(); 26 | } 27 | return null; 28 | } 29 | } 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/MonthlyBadge.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.*; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.relational.core.mapping.Column; 7 | import org.springframework.data.relational.core.mapping.Table; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @With 15 | @Table("monthly_badge") 16 | @EqualsAndHashCode(callSuper = false) 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class MonthlyBadge extends AuditDomain { 20 | 21 | @Id 22 | @Column("id") 23 | private UUID id; 24 | 25 | @Column("badge") 26 | private String badge; 27 | 28 | @Column("badge_index") 29 | private Integer badgeIndex; 30 | 31 | @Column("badge_value") 32 | private BigDecimal badgeValue; 33 | 34 | @Column("status") 35 | private StatusType status; 36 | } -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20230310_search_goods.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE goods_search 2 | ( 3 | id uuid DEFAULT uuid_generate_v4() primary key, 4 | track_date int not null, 5 | track_or_customer_id uuid not null, 6 | keyword varchar(100) not null, 7 | status varchar(15) not null, 8 | created_date timestamptz 9 | ); 10 | 11 | CREATE INDEX idx_goods_search_track_date_other ON goods_search (track_date, track_or_customer_id, keyword); 12 | CREATE INDEX idx_goods_search_track_or_customer_id ON goods_search (track_or_customer_id); 13 | 14 | CREATE TABLE goods_search_count 15 | ( 16 | id uuid DEFAULT uuid_generate_v4() primary key, 17 | keyword varchar(100) not null, 18 | used_count int, 19 | status varchar(15) not null 20 | ); 21 | CREATE UNIQUE INDEX idx_keyword_count_keyword ON goods_search_count (keyword, status); 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/GoodsSearchRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.GoodsSearch; 4 | import org.springframework.data.r2dbc.repository.Query; 5 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 6 | import org.springframework.stereotype.Repository; 7 | import reactor.core.publisher.Flux; 8 | 9 | import java.util.UUID; 10 | 11 | @Repository 12 | public interface GoodsSearchRepository extends R2dbcRepository { 13 | 14 | @Query(""" 15 | with tmp as (select track_or_customer_id, keyword, count(id) 16 | from goods_search gs 17 | where track_or_customer_id = :customer_id 18 | group by track_or_customer_id, keyword) 19 | select * from tmp order by count limit 10 20 | """) 21 | Flux findTopKeywordsByCustomerId(UUID customerId); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/Contactus.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.ContactusStatus; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @Table("contactus") 15 | @EqualsAndHashCode(callSuper = false) 16 | public class Contactus extends AuditDomain { 17 | 18 | @Id 19 | @Column("id") 20 | private UUID id; 21 | 22 | @Column("customer_id") 23 | private UUID customerId; 24 | 25 | @Column("phone_no") 26 | private String phoneNo; 27 | 28 | @Column("query") 29 | private String query; 30 | 31 | @Column("comment") 32 | private String comment; 33 | 34 | @Column("status") 35 | private ContactusStatus status = ContactusStatus.NEW; 36 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/ShippingFeePromo.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.math.BigDecimal; 11 | import java.util.UUID; 12 | 13 | 14 | @Data 15 | @Table("shipping_fee_promo") 16 | @EqualsAndHashCode(callSuper = false) 17 | public class ShippingFeePromo extends AuditDomain { 18 | 19 | @Id 20 | @Column("id") 21 | private UUID id; 22 | 23 | @Column("start_range") 24 | private BigDecimal startRange; 25 | 26 | @Column("end_range") 27 | private BigDecimal endRange; 28 | 29 | @Column("shipping_fee") 30 | private BigDecimal shippingFee; 31 | 32 | @Column("status") 33 | private StatusType status = StatusType.ENABLE; 34 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/util/DateTime.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.util; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.time.Instant; 5 | import java.time.LocalDateTime; 6 | import java.time.ZoneId; 7 | import java.util.Date; 8 | 9 | /** 10 | * Our data base Timezone is SingaporeTime 11 | */ 12 | public class DateTime { 13 | 14 | static String VN_TIME_ZONE = "Asia/Ho_Chi_Minh"; 15 | 16 | 17 | public static Long currentTimeToMillis() { 18 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmsss"); 19 | System.out.println(sdf.format(new Date())); 20 | return Long.parseLong(sdf.format(new Date())); 21 | } 22 | 23 | public static Long currentDateAsLong() { 24 | SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHH"); 25 | return Long.parseLong(sdf.format(new Date())); 26 | } 27 | 28 | public static void main(String[] args) { 29 | System.out.println(currentTimeToMillis()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/test/resources/json/saved-order-cart.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "configName": "basket_seq", 4 | "configValue": "1", 5 | "rewardType": "FIRST_SIGNUP_BONUS", 6 | "rangeList": [ 7 | { 8 | "id": "", 9 | "startRange": "0", 10 | "endRange": "100000", 11 | "discountPrice": "40000" 12 | }, 13 | { 14 | "id": "", 15 | "startRange": "100000", 16 | "endRange": "10000000", 17 | "discountPrice": "100000" 18 | } 19 | ] 20 | }, 21 | { 22 | "configName": "basket_seq", 23 | "configValue": "2", 24 | "rewardType": "SECOND_SIGNUP_BONUS", 25 | "rangeList": [ 26 | { 27 | "id": "", 28 | "startRange": "1900", 29 | "endRange": "41900", 30 | "discountPrice": "200000" 31 | }, 32 | { 33 | "id": "", 34 | "startRange": "1900", 35 | "endRange": "41900", 36 | "discountPrice": "200000" 37 | } 38 | ] 39 | } 40 | ] 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/LuckyDrawContestResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | import com.sellde.reward.service.LuckyDrawContestService; 4 | import com.sellde.reward.service.biz.StorePromoBizService; 5 | import com.sellde.reward.service.model.LuckyDrawContestModel; 6 | import com.sellde.reward.service.model.StorePromoModel; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.*; 10 | import reactor.core.publisher.Mono; 11 | 12 | @RestController 13 | @RequestMapping("v1/open/lucky-draw-contest") 14 | public class LuckyDrawContestResource { 15 | 16 | @Autowired 17 | private LuckyDrawContestService luckyDrawContestService; 18 | 19 | @PostMapping() 20 | @ResponseStatus(HttpStatus.OK) 21 | public Mono create(@RequestBody LuckyDrawContestModel model) { 22 | return luckyDrawContestService.create(model); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/StorePromoResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | import com.sellde.reward.service.biz.StorePromoBizService; 4 | import com.sellde.reward.service.model.StorePromoModel; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.ResponseStatus; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import reactor.core.publisher.Mono; 12 | 13 | @Deprecated 14 | @RestController 15 | @RequestMapping("v1/store-promo") 16 | public class StorePromoResource { 17 | 18 | @Autowired 19 | private StorePromoBizService storePromoBizService; 20 | 21 | @GetMapping() 22 | @ResponseStatus(HttpStatus.OK) 23 | public Mono getStorePromo() { 24 | return storePromoBizService.findPromos(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/OpenStorePromoResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | import com.sellde.reward.service.biz.StorePromoBizService; 4 | import com.sellde.reward.service.model.StorePromoModel; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.http.HttpStatus; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.ResponseStatus; 10 | import org.springframework.web.bind.annotation.RestController; 11 | import reactor.core.publisher.Mono; 12 | 13 | @RestController 14 | @RequestMapping("v1/open/store-promo") 15 | public class OpenStorePromoResource { 16 | 17 | @Autowired 18 | private StorePromoBizService storePromoBizService; 19 | 20 | @GetMapping() 21 | @ResponseStatus(HttpStatus.OK) 22 | public Mono getStorePromo() { 23 | return storePromoBizService.findPromos(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/UserProfileCopy.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @Table("user_profile_copy") 15 | @EqualsAndHashCode(callSuper = false) 16 | public class UserProfileCopy extends AuditDomain { 17 | 18 | @Id 19 | @Column("id") 20 | private UUID id; 21 | 22 | @Column("user_id") 23 | private UUID userId; 24 | 25 | @Column("user_name") 26 | private String userName; 27 | 28 | @Column("phone_no") 29 | private Long phoneNo; 30 | 31 | @Column("subgroup") 32 | private String subgroup; 33 | 34 | @Column("user_type") 35 | private String userType; 36 | 37 | @Column("status") 38 | private StatusType status; 39 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/mapper/SupplierSalesMapper.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.mapper; 2 | 3 | import com.sellde.reward.domain.SupplierSales; 4 | import com.sellde.reward.service.model.SupplierSalesModel; 5 | import org.mapstruct.Mapper; 6 | import org.mapstruct.Mapping; 7 | import org.mapstruct.MappingTarget; 8 | import org.mapstruct.Named; 9 | import org.springframework.stereotype.Component; 10 | 11 | 12 | @Component 13 | @Mapper(componentModel = "spring", uses = {}) 14 | public interface SupplierSalesMapper { 15 | 16 | SupplierSales toEntityFromBasket(SupplierSalesModel.BasketRequest request); 17 | 18 | SupplierSalesModel.BasketResponse toBasketResponse(SupplierSales entity); 19 | 20 | @Named("toUpdateEntity") 21 | @Mapping(target = "id", ignore = true) 22 | @Mapping(target = "createdDate", ignore = true) 23 | @Mapping(target = "lastModifiedDate", ignore = true) 24 | SupplierSales toUpdateEntity(@MappingTarget SupplierSales entity, SupplierSalesModel.BasketRequest request); 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/AuditDomain.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnore; 5 | import lombok.Data; 6 | import org.springframework.data.annotation.CreatedDate; 7 | import org.springframework.data.annotation.LastModifiedDate; 8 | import org.springframework.data.relational.core.mapping.Column; 9 | 10 | import java.io.Serial; 11 | import java.io.Serializable; 12 | import java.time.Instant; 13 | 14 | /** 15 | * Base abstract class for entities which will hold definitions for created, last modified, created by, 16 | * last modified by attributes. 17 | */ 18 | @Data 19 | public abstract class AuditDomain implements Serializable { 20 | 21 | @Serial 22 | private static final long serialVersionUID = 1L; 23 | 24 | @CreatedDate 25 | @Column("created_date") 26 | @JsonIgnore 27 | protected Instant createdDate; 28 | 29 | 30 | @LastModifiedDate 31 | @Column("last_modified_date") 32 | @JsonIgnore 33 | protected Instant lastModifiedDate; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/FlywayConfig.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config; 2 | 3 | import org.flywaydb.core.Flyway; 4 | import org.springframework.beans.factory.annotation.Value; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.context.annotation.Configuration; 7 | 8 | /** 9 | * Database migration 10 | */ 11 | @Configuration 12 | public class FlywayConfig { 13 | 14 | @Value("${spring.flyway.url}") 15 | private String url; 16 | 17 | @Value("${spring.flyway.user}") 18 | private String user; 19 | 20 | @Value("${spring.flyway.password}") 21 | private String password; 22 | 23 | @Bean(initMethod = "migrate") 24 | public Flyway flyway() { 25 | return Flyway.configure() 26 | .locations("db/migration") 27 | .sqlMigrationPrefix("m") 28 | .sqlMigrationSeparator("_") 29 | .table("migration") 30 | .baselineOnMigrate(true) 31 | .dataSource(url, user,password) 32 | .load(); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/GoodsSearch.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.sellde.reward.enums.StatusType; 5 | import lombok.Data; 6 | import org.springframework.data.annotation.CreatedDate; 7 | import org.springframework.data.annotation.Id; 8 | import org.springframework.data.relational.core.mapping.Column; 9 | import org.springframework.data.relational.core.mapping.Table; 10 | 11 | import java.time.Instant; 12 | import java.util.UUID; 13 | 14 | 15 | @Data 16 | @Table("goods_search") 17 | public class GoodsSearch { 18 | 19 | @Id 20 | @Column("id") 21 | private UUID id; 22 | 23 | @Column("track_date") 24 | private Long trackDate; 25 | 26 | @Column("track_or_customer_id") 27 | private UUID trackOrCustomerId; 28 | 29 | @Column("keyword") 30 | private String keyword; 31 | 32 | @Column("status") 33 | private StatusType status; 34 | 35 | @CreatedDate 36 | @Column("created_date") 37 | @JsonIgnore 38 | private Instant createdDate; 39 | 40 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/CustomerBadgeRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.CustomerBadge; 4 | import org.springframework.data.domain.Sort; 5 | import org.springframework.data.r2dbc.repository.Query; 6 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 7 | import org.springframework.stereotype.Repository; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.util.List; 12 | import java.util.UUID; 13 | 14 | @Repository 15 | public interface CustomerBadgeRepository extends R2dbcRepository { 16 | 17 | Mono findOneByCustomerIdAndYearMonth(UUID customerId, Integer yearMonth); 18 | 19 | // Flux findAllByYearMonthIsInOrderByCustomerIdYearMonth(List yearMonthList); 20 | 21 | Flux findAllByYearMonthIsIn(List yearMonthList, Sort sort); 22 | 23 | Flux findAllByCustomerIdAndYearMonthIsIn(UUID customerId, List yearMonthList); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/support/MapToJsonConverter.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config.support; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import io.r2dbc.postgresql.codec.Json; 6 | import lombok.AllArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.core.convert.converter.Converter; 9 | import org.springframework.data.convert.WritingConverter; 10 | 11 | import java.util.Map; 12 | 13 | @Slf4j 14 | @WritingConverter 15 | @AllArgsConstructor 16 | public class MapToJsonConverter implements Converter, Json> { 17 | 18 | private final ObjectMapper objectMapper; 19 | 20 | @Override 21 | public Json convert(Map source) { 22 | try { 23 | return Json.of(objectMapper.writeValueAsString(source)); 24 | } catch (JsonProcessingException e) { 25 | log.error("Error occurred while serializing map to JSON: {}", source, e); 26 | } 27 | return Json.of(""); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/SignupQnA.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @Table("signup_qna") 15 | @EqualsAndHashCode(callSuper = false) 16 | public class SignupQnA extends AuditDomain { 17 | 18 | @Id 19 | @Column("id") 20 | private UUID id; 21 | 22 | @Column("phone_no") 23 | private long phoneNo; 24 | 25 | @Column("query_no") 26 | private int queryNo; 27 | 28 | @Column("query_en") 29 | private String queryEn; 30 | 31 | @Column("query_vn") 32 | private String queryVn; 33 | 34 | @Column("answer") 35 | private String answer; 36 | 37 | @Column("status") 38 | private StatusType status; 39 | 40 | public SignupQnA() { 41 | status = StatusType.ENABLE; 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/DefaultResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.ResponseStatus; 7 | import org.springframework.web.bind.annotation.RestController; 8 | import reactor.core.publisher.Mono; 9 | 10 | import java.util.HashMap; 11 | 12 | @RestController 13 | @RequestMapping 14 | public class DefaultResource { 15 | 16 | @GetMapping("/default/health-check") 17 | @ResponseStatus(HttpStatus.OK) 18 | public Mono> getHealthCheck() { 19 | var map = new HashMap(); 20 | map.put("status", "success"); 21 | map.put("value", "health is good"); 22 | return Mono.just(map); 23 | } 24 | 25 | @GetMapping("/api/v2/index") 26 | @ResponseStatus(HttpStatus.OK) 27 | public Mono> getHealthCheckIndex() { 28 | return getHealthCheck(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/resources/config/application-prod.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: SupplyRewardEngine 4 | 5 | r2dbc: 6 | database: supply_reward 7 | username: supplyrewarduser 8 | password: ${DB_PASSWORD} 9 | url: r2dbc:postgresql://db-prod-442xsw5pk7.sellde.cloud:9432/${spring.r2dbc.database} 10 | pool: 11 | enabled: true 12 | initial-size: 3 13 | max-size: 20 14 | validation-query: "select 1" 15 | max-idle-time: 10 16 | 17 | 18 | flyway: 19 | user: ${spring.r2dbc.username} 20 | password: ${spring.r2dbc.password} 21 | url: jdbc:postgresql://db-prod-442xsw5pk7.sellde.cloud:9432/${spring.r2dbc.database} 22 | 23 | security: 24 | oauth2: 25 | resourceserver: 26 | jwt: 27 | issuer-uri: https://cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_vKv7cQbv4 28 | 29 | webflux: 30 | base-path: /supply-reward 31 | 32 | server: 33 | port: 80 34 | 35 | sellde: 36 | api-user-product: https://titan.sellde.cloud/user-product 37 | supply-reward-api-key: PROD-30203P0QCAQ75U6216D507X8QV2LM06UO3D5WE0HN73C8270E -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/MonthlyBadgeBrandConfig.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.*; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.relational.core.mapping.Column; 7 | import org.springframework.data.relational.core.mapping.Table; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @With 15 | @Table("monthly_badge_brand_config") 16 | @EqualsAndHashCode(callSuper = false) 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class MonthlyBadgeBrandConfig extends AuditDomain { 20 | 21 | @Id 22 | @Column("id") 23 | private UUID id; 24 | 25 | @Column("goods_brand_id") 26 | private UUID goodsBrandId; 27 | 28 | @Column("goods_brand_name") 29 | private String goodsBrandName; 30 | 31 | @Column("monthly_badge_id") 32 | private UUID monthlyBadgeId; 33 | 34 | @Column("badge_discount_rate") 35 | private BigDecimal badgeDiscountRate; 36 | 37 | @Column("status") 38 | private StatusType status; 39 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/MonthlyBadgeRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.MonthlyBadge; 4 | import com.sellde.reward.enums.StatusType; 5 | import org.springframework.data.domain.PageRequest; 6 | import org.springframework.data.r2dbc.repository.Query; 7 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 8 | import org.springframework.stereotype.Repository; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.math.BigDecimal; 13 | import java.util.UUID; 14 | 15 | 16 | @Repository 17 | public interface MonthlyBadgeRepository extends R2dbcRepository { 18 | 19 | @Query("select * from monthly_badge mb where mb.badge_value <= :badgeValue and status = 'ENABLE' order by badge_index desc limit 1") 20 | Mono findAllByBadgeValueLessThanEqualAndStatus(BigDecimal badgeValue); 21 | 22 | Flux findAllByStatus(StatusType status); 23 | 24 | Mono findOneByBadgeAndStatus(String badge, StatusType statusType); 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/CouponTracker.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.sellde.reward.enums.StatusType; 5 | import lombok.Data; 6 | import lombok.EqualsAndHashCode; 7 | import org.springframework.data.annotation.CreatedDate; 8 | import org.springframework.data.annotation.Id; 9 | import org.springframework.data.relational.core.mapping.Column; 10 | import org.springframework.data.relational.core.mapping.Table; 11 | 12 | import java.time.Instant; 13 | import java.util.UUID; 14 | 15 | 16 | @Data 17 | @Table("coupon_tracker") 18 | @EqualsAndHashCode(callSuper = false) 19 | public class CouponTracker { 20 | 21 | @Id 22 | @Column("id") 23 | private UUID id; 24 | 25 | @Column("customer_id") 26 | private UUID customerId; 27 | 28 | @Column("discount_coupon_id") 29 | private UUID discountCouponId; 30 | 31 | @Column("status") 32 | private StatusType status = StatusType.ENABLE; 33 | 34 | @CreatedDate 35 | @Column("created_date") 36 | @JsonIgnore 37 | protected Instant createdDate = Instant.now(); 38 | 39 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/UserModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.*; 5 | import lombok.experimental.FieldDefaults; 6 | 7 | import java.util.UUID; 8 | 9 | public class UserModel { 10 | 11 | @Data 12 | @With 13 | @AllArgsConstructor 14 | @FieldDefaults(level = AccessLevel.PRIVATE) 15 | public static class Request { 16 | UUID id; 17 | UUID userId; 18 | long mobileNo; 19 | String name; 20 | String type; 21 | String subgroup; 22 | StatusType status; 23 | 24 | public Request() { 25 | status = StatusType.ENABLE; 26 | } 27 | } 28 | 29 | @Data 30 | @With 31 | @AllArgsConstructor 32 | @NoArgsConstructor 33 | public static class ProfileModel { 34 | private UUID id; 35 | private UUID userId; 36 | private String userName; 37 | private long phoneNo; 38 | private String userType; 39 | private String subgroup; 40 | private StatusType status; 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/support/JsonToMapConverter.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config.support; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import io.r2dbc.postgresql.codec.Json; 6 | import lombok.AllArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.core.convert.converter.Converter; 9 | import org.springframework.data.convert.ReadingConverter; 10 | 11 | import java.io.IOException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | @Slf4j 16 | @ReadingConverter 17 | @AllArgsConstructor 18 | public class JsonToMapConverter implements Converter> { 19 | 20 | private final ObjectMapper objectMapper; 21 | 22 | @Override 23 | public Map convert(Json json) { 24 | try { 25 | return objectMapper.readValue(json.asString(), new TypeReference<>() { 26 | }); 27 | } catch (IOException e) { 28 | log.error("Problem while parsing JSON: {}", json, e); 29 | } 30 | return new HashMap<>(); 31 | } 32 | 33 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/SupplierSalesRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.domain.SupplierSales; 4 | import com.sellde.reward.enums.SelectOption; 5 | import org.springframework.data.r2dbc.repository.Query; 6 | import org.springframework.data.r2dbc.repository.R2dbcRepository; 7 | import org.springframework.stereotype.Repository; 8 | import reactor.core.publisher.Mono; 9 | 10 | import java.util.UUID; 11 | 12 | 13 | @Repository 14 | public interface SupplierSalesRepository extends R2dbcRepository { 15 | 16 | Mono findByOrderNo(Integer orderNo); 17 | 18 | @Query(""" 19 | select * from basket_sales 20 | where order_no = 21 | ( 22 | select max(order_no) 23 | from basket_sales bs 24 | where bs.customer_id = :customerId 25 | and bs.basket_status not in ('ORDER_PLACED', 'ORDER_CANCEL', 'ORDER_REJECT', 'ORDER_INVALID') 26 | ) 27 | """) 28 | Mono findMaxValidOrderForCustomer(UUID customerId); 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/config/application-dev.yml: -------------------------------------------------------------------------------- 1 | aws: 2 | region: ap-southeast-1 3 | user-pool: ap-southeast-1_U3EdmFdGS 4 | 5 | spring: 6 | application: 7 | name: CartEngine 8 | 9 | r2dbc: 10 | database: supply_reward 11 | username: devsupplyrewarduser 12 | password: Z0NQHCYZY06QYMAAPTDWSFEPC@## 13 | url: r2dbc:postgresql://db-develop.sellde.cloud:5432/${spring.r2dbc.database} 14 | pool: 15 | enabled: true 16 | initial-size: 3 17 | max-size: 20 18 | validation-query: "select 1" 19 | max-idle-time: 10 20 | 21 | flyway: 22 | user: ${spring.r2dbc.username} 23 | password: ${spring.r2dbc.password} 24 | url: jdbc:postgresql://db-develop.sellde.cloud:5432/${spring.r2dbc.database} 25 | 26 | security: 27 | oauth2: 28 | resourceserver: 29 | jwt: 30 | issuer-uri: https://cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_FE2b1a05d 31 | 32 | webflux: 33 | base-path: /supply-reward 34 | 35 | server: 36 | port: 80 37 | 38 | sellde: 39 | api-user-product: https://titan-dev.sellde.cloud/user-product 40 | supply-reward-api-key: DEV-fc2f9035-42ee-41da-8edf-cd2b4815f5f2 -------------------------------------------------------------------------------- /src/main/resources/config/application-localdev.yml: -------------------------------------------------------------------------------- 1 | aws: 2 | region: ap-southeast-1 3 | user-pool: ap-southeast-1_U3EdmFdGS 4 | 5 | spring: 6 | application: 7 | name: CartEngine 8 | 9 | r2dbc: 10 | database: supply_reward 11 | username: devsupplyrewarduser 12 | password: Z0NQHCYZY06QYMAAPTDWSFEPC@## 13 | url: r2dbc:postgresql://db-develop.sellde.cloud:5432/${spring.r2dbc.database} 14 | pool: 15 | enabled: true 16 | initial-size: 3 17 | max-size: 20 18 | validation-query: "select 1" 19 | max-idle-time: 10 20 | 21 | flyway: 22 | user: ${spring.r2dbc.username} 23 | password: ${spring.r2dbc.password} 24 | url: jdbc:postgresql://db-develop.sellde.cloud:5432/${spring.r2dbc.database} 25 | 26 | security: 27 | oauth2: 28 | resourceserver: 29 | jwt: 30 | issuer-uri: https://cognito-idp.ap-southeast-1.amazonaws.com/ap-southeast-1_FE2b1a05d 31 | 32 | webflux: 33 | base-path: /supply-reward 34 | 35 | server: 36 | port: 80 37 | 38 | sellde: 39 | api-user-product: https://titan-dev.sellde.cloud/user-product 40 | supply-reward-api-key: DEV-fc2f9035-42ee-41da-8edf-cd2b4815f5f2 -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/ContactusService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.repository.ContactusRepository; 4 | import com.sellde.reward.service.mapper.ContactusMapper; 5 | import com.sellde.reward.service.model.ContactusModel; 6 | import com.sellde.reward.util.Utils; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.time.LocalDate; 12 | import java.time.format.DateTimeFormatter; 13 | 14 | @Service 15 | public class ContactusService { 16 | 17 | @Autowired 18 | private ContactusRepository repository; 19 | 20 | @Autowired 21 | private ContactusMapper contactusMapper; 22 | 23 | public Mono create(ContactusModel model) { 24 | var phoneNo = Utils.cleanMobileNo(model.getPhoneNo()); 25 | model.setPhoneNo(phoneNo); 26 | var today = LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); 27 | return repository.save(contactusMapper.toEntity(model)) 28 | .map(contactusMapper::toModel); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/SignupReward.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.math.BigDecimal; 11 | import java.util.UUID; 12 | 13 | 14 | @Data 15 | @Table("signup_reward") 16 | @EqualsAndHashCode(callSuper = false) 17 | public class SignupReward extends AuditDomain { 18 | 19 | @Id 20 | @Column("id") 21 | private UUID id; 22 | 23 | @Column("reward_type") 24 | private String rewardType; 25 | 26 | @Column("config_name") 27 | private String configName; 28 | 29 | @Column("config_value") 30 | private String configValue; 31 | 32 | @Column("start_range") 33 | private BigDecimal startRange; 34 | 35 | @Column("end_range") 36 | private BigDecimal endRange; 37 | 38 | @Column("discount_price") 39 | private BigDecimal discountPrice; 40 | 41 | @Column("status") 42 | private StatusType status; 43 | 44 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/OpenGoodsSearchResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | 4 | import com.sellde.reward.service.biz.GoodsSearchBizService; 5 | import com.sellde.reward.service.model.GoodsSearchModel; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | import reactor.core.publisher.Mono; 9 | 10 | import java.util.List; 11 | import java.util.UUID; 12 | 13 | @RestController 14 | @RequestMapping("v1/open/goods-search") 15 | public class OpenGoodsSearchResource { 16 | 17 | @Autowired 18 | private GoodsSearchBizService goodsSearchBizService; 19 | 20 | @PostMapping 21 | public Mono create(GoodsSearchModel.Request request) { 22 | return goodsSearchBizService.createAndUpdateCount(request); 23 | } 24 | 25 | @GetMapping("/top-10-keyword") 26 | public Mono> topKeywords(@RequestParam(required = false) UUID customerId) { 27 | if (customerId != null) { 28 | return goodsSearchBizService.top10KeywordByCustomerId(customerId); 29 | } 30 | return goodsSearchBizService.top10Keyword(); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/util/SwaggerConstant.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.util; 2 | 3 | public class SwaggerConstant { 4 | public static final String BUYER_ORDER_CART = "Cart Order API for Buyer"; 5 | public static final String SELLER_ORDER_CART = "Cart Order API for Seller"; 6 | public static final String BUYER_SHIPPING = "Shipping Address API for Buyer"; 7 | public static final String STORE_SHIPPING_METHOD= "Shipping methods of Store API"; 8 | public static final String STORE_ORDER_CART = "Cart Order API for Store"; 9 | public static final String STORE_SALES = "Sales detail API for Store"; 10 | 11 | 12 | public static final String BUYER_ORDER_CART_V1 = "V1 Cart Order API for Buyer"; 13 | public static final String SELLER_ORDER_CART_V1 = "V1 Cart Order API for Seller"; 14 | public static final String BUYER_SHIPPING_V1 = "V1 Shipping Address API for Buyer"; 15 | public static final String STORE_SHIPPING_METHOD_V1 = "V1 Shipping methods of Store API"; 16 | public static final String STORE_ORDER_CART_V1 = "V1 Cart Order API for Store"; 17 | public static final String STORE_SALES_V1 = "V1 Sales detail API for Store"; 18 | 19 | private SwaggerConstant() { 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20221101_alter_user_copy.sql: -------------------------------------------------------------------------------- 1 | drop table if exists user_profile_copy_bak; 2 | 3 | -- create temporary table 4 | CREATE TABLE user_profile_copy_bak AS 5 | SELECT * 6 | FROM user_profile_copy; 7 | 8 | drop table if exists user_profile_copy; 9 | 10 | CREATE TABLE user_profile_copy 11 | ( 12 | id uuid DEFAULT uuid_generate_v4() primary key, 13 | user_id uuid NOT NULL, 14 | user_name varchar(100), 15 | phone_no int default 0, 16 | user_type varchar(15) not null, 17 | subgroup varchar(20) not null, 18 | status varchar(15) not null, 19 | created_date timestamptz, 20 | last_modified_date timestamptz 21 | ); 22 | CREATE UNIQUE INDEX idx_user_profile_copy_user_id ON user_profile_copy (user_id); 23 | 24 | 25 | -- populate user_profile_copy 26 | INSERT INTO user_profile_copy 27 | (id, user_id, user_name, phone_no, user_type, subgroup, status, created_date, last_modified_date) 28 | select id, user_id, user_name, phone_no, user_type, 'EXTERNAL', status, created_date, last_modified_date 29 | from user_profile_copy_bak; 30 | 31 | 32 | drop table if exists user_profile_copy_bak; 33 | 34 | commit; 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/CouponTrackerResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | import com.sellde.reward.service.CouponTrackerService; 4 | import com.sellde.reward.service.model.CouponTrackerModel; 5 | import org.json.JSONObject; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.*; 8 | import reactor.core.publisher.Mono; 9 | 10 | import java.util.Map; 11 | import java.util.UUID; 12 | 13 | @RestController 14 | @RequestMapping("v1/coupon-tracker") 15 | public class CouponTrackerResource { 16 | 17 | @Autowired 18 | private CouponTrackerService couponTrackerService; 19 | 20 | @PostMapping() 21 | public Mono customerAvailablePromo(@RequestBody CouponTrackerModel model) { 22 | return couponTrackerService.createUsingCouponName(model); 23 | } 24 | 25 | @GetMapping("/can-apply-coupon") 26 | public Mono canCustomerApplyCoupon(@RequestParam UUID customerId, @RequestParam String couponName) { 27 | return couponTrackerService.canCustomerApplyCoupon(customerId, couponName) 28 | .map(flag -> new JSONObject() 29 | .put("canApplyCoupon", flag).toMap()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/security/AuthenticatedUser.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.security; 2 | 3 | import org.springframework.security.core.context.SecurityContextHolder; 4 | import org.springframework.stereotype.Component; 5 | 6 | import org.springframework.security.core.context.ReactiveSecurityContextHolder; 7 | import org.springframework.security.oauth2.jwt.Jwt; 8 | import org.springframework.stereotype.Component; 9 | import reactor.core.publisher.Mono; 10 | 11 | import static com.sellde.reward.util.Utils.emptyString; 12 | import static com.sellde.reward.util.Utils.isNotNull; 13 | 14 | @Component 15 | public class AuthenticatedUser { 16 | 17 | public Mono getReactiveJwtToken() { 18 | return ReactiveSecurityContextHolder.getContext() 19 | .map(ctx -> { 20 | var auth = ctx.getAuthentication(); 21 | return isNotNull(auth) ? 22 | ((Jwt) auth.getCredentials()).getTokenValue() 23 | : emptyString(); 24 | }); 25 | } 26 | 27 | public Mono getReactiveJwtToken(StringBuffer jwtToken) { 28 | return getReactiveJwtToken() 29 | .map(token -> jwtToken.append(token).toString()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/util/Constant.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.util; 2 | 3 | 4 | public class Constant { 5 | public static final String ACTIVE_PROFILE = "spring.profiles.active"; 6 | 7 | public static final String ENV_LOCAL = "local"; 8 | public static final String ENV_LOCAL_DEV = "localdev"; 9 | public static final String ENV_TEST = "test"; 10 | public static final String ENV_PROD = "prod"; 11 | 12 | public static final String STAFF = "staff"; 13 | public static final int DEFAULT_PAGE_SIZE = 100; 14 | public static final String DEFAULT_SORT_ORDER = "DESC"; 15 | public static final String DESC_SORT = "DESC"; 16 | public static final String ASC_SORT = "ASC"; 17 | public static final int CART_DESC_MAX_LENGTH = 500; 18 | public static final String ISO_UTC_DATE_TIME_FORMAT = "YYYY-MM-DDTHH:mm:ss.sssZ"; 19 | 20 | public static final Long SYSTEM_PHONE_NO = 999999999L; 21 | public static String SHIPPING_PROMO_CONFIG = "isStoreShippingFeePromoOn"; 22 | public static String SIGNUP_PROMO_CONFIG = "isStoreSignupDiscountPromoOn"; 23 | public static String DISCOUNT_COUPON_CONFIG = "isStoreDiscountCouponPromoOn"; 24 | public static String NOT_AVIALBLE = "NA"; 25 | 26 | private Constant() { 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/SwaggerConfig.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import springfox.documentation.builders.ApiInfoBuilder; 6 | import springfox.documentation.builders.PathSelectors; 7 | import springfox.documentation.service.ApiInfo; 8 | import springfox.documentation.spi.DocumentationType; 9 | import springfox.documentation.spring.web.plugins.Docket; 10 | import springfox.documentation.swagger2.annotations.EnableSwagger2WebFlux; 11 | 12 | @Configuration 13 | @EnableSwagger2WebFlux 14 | public class SwaggerConfig { 15 | 16 | // swagger path - http://localhost:8080/swagger-ui.html#/ 17 | private ApiInfo apiInfo() { 18 | return new ApiInfoBuilder() 19 | .title("Supply Reward Service") 20 | .description("Supply Reward API Documentation") 21 | .version("1.0") 22 | .build(); 23 | } 24 | 25 | @Bean 26 | public Docket docket() { 27 | return new Docket(DocumentationType.SWAGGER_2) 28 | .apiInfo(this.apiInfo()) 29 | .enable(true) 30 | .select() 31 | .paths(PathSelectors.any()) 32 | .build(); 33 | } 34 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/LuckyDrawContestService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.repository.LuckyDrawContestRepository; 4 | import com.sellde.reward.service.mapper.LuckyDrawContestMapper; 5 | import com.sellde.reward.service.model.LuckyDrawContestModel; 6 | import com.sellde.reward.util.Utils; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.time.LocalDate; 12 | import java.time.format.DateTimeFormatter; 13 | 14 | @Service 15 | public class LuckyDrawContestService { 16 | 17 | @Autowired 18 | private LuckyDrawContestRepository repository; 19 | 20 | @Autowired 21 | private LuckyDrawContestMapper luckyDrawContestMapper; 22 | 23 | public Mono create(LuckyDrawContestModel model) { 24 | var phoneNo = Utils.cleanMobileNo(model.getPhoneNo()); 25 | model.setPhoneNo(phoneNo); 26 | var today = LocalDate.now().format(DateTimeFormatter.ofPattern("dd-MM-yyyy")); 27 | return repository.findBYPhoneAndDay(phoneNo, today) 28 | .switchIfEmpty(repository.save(luckyDrawContestMapper.toEntity(model))) 29 | .map(luckyDrawContestMapper::toModel); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/test/java/com/sellde/inte/LuckyDrawContestResourceTest.java: -------------------------------------------------------------------------------- 1 | package com.sellde.inte; 2 | 3 | import com.sellde.reward.service.model.LuckyDrawContestModel; 4 | import org.junit.jupiter.api.Test; 5 | import org.junit.jupiter.api.TestInstance; 6 | import org.springframework.http.MediaType; 7 | import reactor.core.publisher.Mono; 8 | 9 | 10 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 11 | class LuckyDrawContestResourceTest extends IntegrationTest { 12 | 13 | @Test 14 | public void testCreate() { 15 | System.out.println("Running test case."); 16 | 17 | var request = new LuckyDrawContestModel(); 18 | request.setPhoneNo("+84909990919"); 19 | 20 | 21 | System.out.println("Request -> "+ printAsJson(request)); 22 | 23 | var responseModel = webTestClient 24 | .post() 25 | .uri("/v1/open/lucky-draw-contest") 26 | .body(Mono.just(request), LuckyDrawContestModel.class) 27 | .exchange() 28 | .expectStatus().isOk() 29 | .expectHeader().valueEquals("Content-Type", MediaType.APPLICATION_JSON_VALUE) 30 | .expectBody(LuckyDrawContestModel.class) 31 | .returnResult().getResponseBody(); 32 | 33 | System.out.println("Response -> " + printAsJson(responseModel)); 34 | } 35 | 36 | 37 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/OpenCustomerBadgeResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | 4 | import com.sellde.reward.service.CustomerBadgeService; 5 | import com.sellde.reward.service.MonthlyBadgeService; 6 | import com.sellde.reward.service.model.CustomerBadgeModel; 7 | import com.sellde.reward.service.model.MonthlyBadgeModel; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import reactor.core.publisher.Flux; 14 | import reactor.core.publisher.Mono; 15 | 16 | import java.math.BigDecimal; 17 | import java.util.HashMap; 18 | import java.util.UUID; 19 | 20 | @RestController 21 | @RequestMapping("v1/open/customer-badge") 22 | public class OpenCustomerBadgeResource { 23 | 24 | @Autowired 25 | private CustomerBadgeService customerBadgeService; 26 | 27 | @Autowired 28 | private MonthlyBadgeService monthlyBadgeService; 29 | 30 | @GetMapping("/range") 31 | public Flux getByParams() { 32 | // return customerBadgeService.getBadgeValues(); 33 | return monthlyBadgeService.getAllEnabled(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20210101_baseline.sql: -------------------------------------------------------------------------------- 1 | CREATE 2 | EXTENSION IF NOT EXISTS "uuid-ossp"; 3 | 4 | 5 | CREATE TABLE shipping_fee_promo 6 | ( 7 | id uuid DEFAULT uuid_generate_v4() primary key, 8 | start_range numeric(12, 2) default 0, 9 | end_range numeric(12, 2) default 0, 10 | shipping_fee numeric(12, 2) default 0, 11 | status varchar(15), 12 | created_date timestamptz, 13 | last_modified_date timestamptz 14 | ); 15 | 16 | CREATE TABLE setting 17 | ( 18 | id uuid DEFAULT uuid_generate_v4() primary key, 19 | config_type varchar(350), 20 | config_name varchar(350), 21 | config_value varchar(350) default 'NOT_AVAILABLE', 22 | status varchar(50), 23 | created_date timestamptz, 24 | last_modified_date timestamptz, 25 | UNIQUE (config_type, config_name) 26 | ); 27 | 28 | -- static data 29 | INSERT INTO public.setting (config_type, config_name, config_value, status, created_date, last_modified_date) 30 | VALUES ('SHIPPING_PROMO', 'isStoreShippingFeePromoOn', 'true', 'ENABLE', 'now()', 'now()'); 31 | 32 | 33 | INSERT INTO public.shipping_fee_promo (start_range, end_range, shipping_fee, status, created_date, last_modified_date) 34 | VALUES (0, 500000.00, 30000, 'ENABLE', 'now()', 'now()'); 35 | -------------------------------------------------------------------------------- /src/test/resources/json/create-order-cart.json: -------------------------------------------------------------------------------- 1 | { 2 | "cartId": null, 3 | "storeId": "53bb36cc-ebe7-43af-a545-7ef52c36ae9a", 4 | "buyerUserId": "41bb36cc-ebe7-43af-a545-7ef52c36a399", 5 | "buyerMobileNo": "+84900909090", 6 | "buyerName": "Rajesh Kumar", 7 | "orderNo": "", 8 | "totalPrice": 16000.50, 9 | "shippingPrice": 40000.50, 10 | "discountPrice": 10000.50, 11 | "coupon": "S100", 12 | "paymentOption": "CASH_ON_DELIVERY", 13 | "cartItems": [ 14 | { 15 | "productId": "23f5d84e-c64f-48e3-a283-c13ac6b831cb", 16 | "quantity": 2, 17 | "productMrp": 90000.50, 18 | "productPrice": 80000.50, 19 | "productName": "The makeup box", 20 | "productImagePath": "foundation_box.jpg", 21 | "note": "Pack in nice red box" 22 | }, 23 | { 24 | "productId": "5e29dfd4-896d-43e8-b86a-37c226487efb", 25 | "quantity": 4, 26 | "productMrp": 70000.50, 27 | "productPrice": 60000.50, 28 | "productName": "The lipstick box", 29 | "productImagePath": "lipstick.jpg", 30 | "note": "Pack in nice yellow box" 31 | } 32 | ], 33 | "shipping": { 34 | "address": "123, Saigon Street, Hochiminh,Vietnam", 35 | "mapAddress": { 36 | "lat": 21, 37 | "lng": 54 38 | }, 39 | "landmark": "Near the Church", 40 | "notes": null, 41 | "method": "DELIVERY" 42 | }, 43 | "cartStatus": null 44 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/DiscountCoupon.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.math.BigDecimal; 11 | import java.time.LocalDate; 12 | import java.util.UUID; 13 | 14 | 15 | @Data 16 | @Table("discount_coupon") 17 | @EqualsAndHashCode(callSuper = false) 18 | public class DiscountCoupon extends AuditDomain { 19 | 20 | @Id 21 | @Column("id") 22 | private UUID id; 23 | 24 | @Column("coupon_name") 25 | private String couponName; 26 | 27 | @Column("description") 28 | private String description; 29 | 30 | @Column("has_fixed_time") 31 | private Boolean hasFixedTime; 32 | 33 | @Column("coupon_price") 34 | private BigDecimal couponPrice; 35 | 36 | @Column("display_no") 37 | private Integer displayNo; 38 | 39 | @Column("customer_count") 40 | private Integer customerCount; 41 | 42 | @Column("min_buying_range") 43 | private BigDecimal minBuyingRange; 44 | 45 | @Column("start_date") 46 | private LocalDate startDate; 47 | 48 | @Column("end_date") 49 | private LocalDate endDate; 50 | 51 | @Column("status") 52 | private StatusType status = StatusType.ENABLE; 53 | 54 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/CustomerBadgeResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | 4 | import com.sellde.reward.service.CustomerBadgeService; 5 | import com.sellde.reward.service.biz.CustomerBadgeReportService; 6 | import com.sellde.reward.service.model.CustomerBadgeModel; 7 | import com.sellde.reward.service.model.CustomerBadgeReport; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.GetMapping; 10 | import org.springframework.web.bind.annotation.RequestMapping; 11 | import org.springframework.web.bind.annotation.RequestParam; 12 | import org.springframework.web.bind.annotation.RestController; 13 | import reactor.core.publisher.Flux; 14 | import reactor.core.publisher.Mono; 15 | 16 | import java.util.UUID; 17 | 18 | @RestController 19 | @RequestMapping("v1/customer-badge") 20 | public class CustomerBadgeResource { 21 | 22 | @Autowired 23 | private CustomerBadgeService customerBadgeService; 24 | 25 | @Autowired 26 | private CustomerBadgeReportService customerBadgeReportService; 27 | 28 | @GetMapping() 29 | public Mono getByParams(@RequestParam UUID customerId) { 30 | return customerBadgeService.getByCustomerIdAndCurrentMonth(customerId); 31 | } 32 | 33 | @GetMapping("/report") 34 | public Flux getMonthlyReport() { 35 | return customerBadgeReportService.createReport(); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/userproduct/UserProductHttpClient.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.userproduct; 2 | 3 | import com.sellde.reward.config.RestWebclient; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | import reactor.core.publisher.Mono; 8 | 9 | @Component 10 | @Slf4j 11 | public class UserProductHttpClient { 12 | 13 | @Autowired 14 | private RestWebclient restWebclient; 15 | 16 | public Mono get(String apiPath, Object... uriVariables) { 17 | return restWebclient 18 | .httpUserProductClient() 19 | .flatMap(webClient -> webClient.get() 20 | .uri(apiPath, uriVariables) 21 | .exchangeToMono(clientResponse -> { 22 | return clientResponse.bodyToMono(String.class); 23 | }) 24 | ); 25 | } 26 | 27 | public Mono get(String apiPath, Class t, Object... uriVariables) { 28 | return restWebclient 29 | .httpUserProductClient() 30 | .flatMap(webClient -> webClient.get() 31 | .uri(apiPath, uriVariables) 32 | .exchangeToMono(clientResponse -> { 33 | return clientResponse.bodyToMono(t); 34 | }) 35 | ); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/RestWebclient.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config; 2 | 3 | import com.sellde.reward.security.AuthenticatedUser; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.http.HttpHeaders; 7 | import org.springframework.http.MediaType; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.web.reactive.function.client.WebClient; 10 | import reactor.core.publisher.Mono; 11 | 12 | @Component 13 | public class RestWebclient { 14 | 15 | @Value("${sellde.api-user-product}") 16 | private String userProductEndpoint; 17 | 18 | @Autowired 19 | private AuthenticatedUser authenticatedUser; 20 | 21 | public Mono httpUserProductClient() { 22 | return authenticatedUser 23 | .getReactiveJwtToken() 24 | .map(token -> WebClient.builder() 25 | .baseUrl(userProductEndpoint) 26 | .defaultHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token) 27 | .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 28 | .build()) 29 | .defaultIfEmpty(WebClient.builder() 30 | .baseUrl(userProductEndpoint) 31 | .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) 32 | .build()); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/ShippingPromoService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import com.sellde.reward.repository.ShippingPromoRepository; 5 | import com.sellde.reward.service.mapper.ShippingPromoMapper; 6 | import com.sellde.reward.service.model.ShippingFeePromoModel; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import reactor.core.publisher.Flux; 10 | 11 | import java.util.List; 12 | 13 | @Service 14 | public class ShippingPromoService { 15 | 16 | @Autowired 17 | private ShippingPromoRepository shippingPromoRepository; 18 | 19 | @Autowired 20 | private ShippingPromoMapper shippingPromoMapper; 21 | 22 | @Autowired 23 | public Flux getAllActivePromos() { 24 | return shippingPromoRepository 25 | .findAllByStatus(StatusType.ENABLE) 26 | .map(shippingPromoMapper::toModel); 27 | } 28 | 29 | @Autowired 30 | public Flux getAllPromos() { 31 | return shippingPromoRepository 32 | .findAll() 33 | .map(shippingPromoMapper::toModel); 34 | } 35 | 36 | public Flux create(List shippingFeePromoList) { 37 | var domainList = shippingPromoMapper.toEntity(shippingFeePromoList); 38 | return shippingPromoRepository.saveAll(domainList) 39 | .map(shippingPromoMapper::toModel); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/OpenSignupQnAResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | import com.sellde.reward.service.SignupQnAService; 4 | import com.sellde.reward.service.model.SignupQnAModel; 5 | import org.json.JSONObject; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.http.HttpStatus; 8 | import org.springframework.web.bind.annotation.*; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | @RestController 16 | @RequestMapping("v1/open/signup_qna") 17 | public class OpenSignupQnAResource { 18 | 19 | @Autowired 20 | private SignupQnAService signupQnAService; 21 | 22 | @GetMapping("/query") 23 | @ResponseStatus(HttpStatus.OK) 24 | public Flux getAllDefaultQuery() { 25 | return signupQnAService.getAllDefaultQuery(); 26 | } 27 | 28 | @PostMapping("/validate") 29 | @ResponseStatus(HttpStatus.OK) 30 | public Mono> createAndValidate(@RequestBody List requestList) { 31 | return signupQnAService.isValidAnswerByCustomer(requestList) 32 | .map(JSONObject::toMap); 33 | } 34 | 35 | @GetMapping("/is-done") 36 | @ResponseStatus(HttpStatus.OK) 37 | public Mono> checkIfAnswered(@RequestParam long phoneNo) { 38 | return signupQnAService.isAnsweredByPhoneNo(phoneNo) 39 | .map(isAnswered -> new JSONObject().put("isAnswered", isAnswered).toMap()); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/GoodsSearchCountService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.domain.GoodsSearchCount; 4 | import com.sellde.reward.enums.StatusType; 5 | import com.sellde.reward.repository.GoodsSearchCountRepository; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.domain.PageRequest; 8 | import org.springframework.data.domain.Sort; 9 | import org.springframework.stereotype.Service; 10 | import reactor.core.publisher.Flux; 11 | import reactor.core.publisher.Mono; 12 | 13 | @Service 14 | public class GoodsSearchCountService { 15 | 16 | @Autowired 17 | private GoodsSearchCountRepository goodsSearchCountRepository; 18 | 19 | public Mono create(String keyword) { 20 | 21 | var newDomain = new GoodsSearchCount(); 22 | newDomain.setKeyword(keyword); 23 | newDomain.setUsedCount(1L); 24 | newDomain.setStatus(StatusType.ENABLE); 25 | 26 | return goodsSearchCountRepository.findOneByKeywordAndStatus(keyword, StatusType.ENABLE) 27 | .map(existing -> { 28 | existing.setUsedCount(existing.getUsedCount() + 1); 29 | return existing; 30 | }) 31 | .defaultIfEmpty(newDomain) 32 | .flatMap(goodsSearchCountRepository::save); 33 | } 34 | 35 | public Flux findTop10Keyword() { 36 | var sort = Sort.by(Sort.Direction.DESC, "usedCount"); 37 | return goodsSearchCountRepository.findAllByStatus(StatusType.ENABLE, PageRequest.of(0, 10, sort)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/GoodsSearchService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.domain.GoodsSearch; 4 | import com.sellde.reward.enums.StatusType; 5 | import com.sellde.reward.repository.GoodsSearchRepository; 6 | import com.sellde.reward.service.mapper.GoodsSearchMapper; 7 | import com.sellde.reward.service.model.GoodsSearchModel; 8 | import com.sellde.reward.util.DateTime; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.data.domain.Example; 11 | import org.springframework.stereotype.Service; 12 | import reactor.core.publisher.Flux; 13 | import reactor.core.publisher.Mono; 14 | 15 | import java.util.List; 16 | import java.util.UUID; 17 | 18 | @Service 19 | public class GoodsSearchService { 20 | 21 | @Autowired 22 | private GoodsSearchRepository goodsSearchRepository; 23 | 24 | @Autowired 25 | private GoodsSearchMapper goodsSearchMapper; 26 | 27 | 28 | public Mono create(GoodsSearchModel.Request request) { 29 | var newDomain = goodsSearchMapper.fromRequest(request); 30 | newDomain.setTrackDate(DateTime.currentDateAsLong()); 31 | newDomain.setStatus(StatusType.ENABLE); 32 | return goodsSearchRepository.findOne(Example.of(newDomain)) 33 | .switchIfEmpty(goodsSearchRepository.save(newDomain)) 34 | .map(item -> new GoodsSearchModel.Result(item.getId())); 35 | } 36 | 37 | 38 | public Flux findKeywordsByCustomerId(UUID customerId) { 39 | return goodsSearchRepository.findTopKeywordsByCustomerId(customerId); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/CustomerBadge.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.*; 5 | import org.springframework.data.annotation.Id; 6 | import org.springframework.data.relational.core.mapping.Column; 7 | import org.springframework.data.relational.core.mapping.Table; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.UUID; 11 | 12 | 13 | @Data 14 | @With 15 | @Table("customer_badge") 16 | @EqualsAndHashCode(callSuper = false) 17 | @AllArgsConstructor 18 | @NoArgsConstructor 19 | public class CustomerBadge extends AuditDomain { 20 | 21 | @Id 22 | @Column("id") 23 | private UUID id; 24 | 25 | @Column("customer_id") 26 | private UUID customerId; 27 | 28 | @Column("year_month") 29 | private Integer yearMonth; 30 | 31 | @Column("total_price") 32 | private BigDecimal totalPrice; 33 | 34 | @Column("total_shipping_price") 35 | private BigDecimal totalShippingPrice; 36 | 37 | @Column("total_discount_price") 38 | private BigDecimal totalDiscountPrice; 39 | 40 | @Column("total_rely_bulk_price") 41 | private BigDecimal totalRelyBulkPrice; 42 | 43 | @Column("default_badge_id") 44 | private UUID defaultBadgeId; 45 | 46 | /* usually calculated based on previous month data */ 47 | @Column("default_badge") 48 | private String defaultBadge; 49 | 50 | @Column("monthly_badge_id") 51 | private UUID monthlyBadgeId; 52 | 53 | @Column("monthly_badge") 54 | private String monthlyBadge; 55 | 56 | @Column("note") 57 | private String note; 58 | 59 | @Column("status") 60 | private StatusType status; 61 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/domain/SupplierSales.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.domain; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import lombok.Data; 5 | import lombok.EqualsAndHashCode; 6 | import org.springframework.data.annotation.Id; 7 | import org.springframework.data.relational.core.mapping.Column; 8 | import org.springframework.data.relational.core.mapping.Table; 9 | 10 | import java.math.BigDecimal; 11 | import java.time.LocalDate; 12 | import java.util.UUID; 13 | 14 | @Data 15 | @Table("basket_sales") 16 | @EqualsAndHashCode(callSuper = false) 17 | public class SupplierSales extends AuditDomain { 18 | 19 | @Id 20 | @Column("id") 21 | private UUID id; 22 | 23 | @Column("customer_id") 24 | private UUID customerId; 25 | 26 | @Column("basket_id") 27 | private UUID basketId; 28 | 29 | @Column("order_date") 30 | private LocalDate orderDate; 31 | 32 | @Column("order_no") 33 | private Integer orderNo; 34 | 35 | @Column("total_price") 36 | private BigDecimal totalPrice; 37 | 38 | @Column("total_rely_bulk_price") 39 | private BigDecimal totalRelyBulkPrice; 40 | 41 | @Column("discount_price") 42 | private BigDecimal discountPrice; 43 | 44 | @Column("shipping_price") 45 | private BigDecimal shippingPrice; 46 | 47 | @Column("select_option") 48 | private String selectOption; 49 | 50 | @Column("pre_customer_badge") 51 | private String preCustomerBadge; 52 | 53 | @Column("post_customer_badge") 54 | private String postCustomerBadge; 55 | 56 | @Column("basket_status") 57 | private String basketStatus; 58 | 59 | @Column("status") 60 | private StatusType status; 61 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/biz/GoodsSearchBizService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.biz; 2 | 3 | import com.sellde.reward.domain.GoodsSearch; 4 | import com.sellde.reward.domain.GoodsSearchCount; 5 | import com.sellde.reward.service.GoodsSearchCountService; 6 | import com.sellde.reward.service.GoodsSearchService; 7 | import com.sellde.reward.service.model.GoodsSearchModel; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import reactor.core.publisher.Flux; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.List; 14 | import java.util.UUID; 15 | 16 | @Service 17 | public class GoodsSearchBizService { 18 | 19 | @Autowired 20 | private GoodsSearchService goodsSearchService; 21 | 22 | @Autowired 23 | private GoodsSearchCountService goodsSearchCountService; 24 | 25 | 26 | public Mono createAndUpdateCount(GoodsSearchModel.Request request) { 27 | return goodsSearchService.create(request) 28 | .zipWith(goodsSearchCountService.create(request.getKeyword())) 29 | .map(tuple -> new GoodsSearchModel.Result(tuple.getT1().getId())); 30 | } 31 | 32 | public Mono> top10Keyword() { 33 | return goodsSearchCountService.findTop10Keyword() 34 | .map(GoodsSearchCount::getKeyword) 35 | .collectList(); 36 | } 37 | 38 | public Mono> top10KeywordByCustomerId(UUID customerId) { 39 | return goodsSearchService.findKeywordsByCustomerId(customerId) 40 | .map(GoodsSearch::getKeyword) 41 | .collectList(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20220928_supplier_sales.sql: -------------------------------------------------------------------------------- 1 | drop table if exists basket_sales; 2 | drop table if exists customer_badge; 3 | 4 | CREATE TABLE basket_sales 5 | ( 6 | id uuid DEFAULT uuid_generate_v4() primary key, 7 | customer_id uuid NOT NULL, 8 | order_date date NOT NULL, 9 | basket_id uuid NOT NULL, 10 | order_no int NOT NULL, 11 | total_price numeric(12, 2) default 0, 12 | total_rely_bulk_price numeric(12, 2) default 0, 13 | discount_price numeric(12, 2) default 0, 14 | shipping_price numeric(12, 2) default 0, 15 | select_option varchar(15), 16 | basket_status varchar(15), 17 | status varchar(15), 18 | created_date timestamptz, 19 | last_modified_date timestamptz 20 | ); 21 | CREATE INDEX idx_basket_sales_customer_id_order_date ON basket_sales (customer_id, order_date); 22 | CREATE UNIQUE INDEX idx_basket_sales_order_no ON basket_sales (order_no); 23 | 24 | 25 | CREATE TABLE customer_badge 26 | ( 27 | id uuid DEFAULT uuid_generate_v4() primary key, 28 | customer_id uuid NOT NULL, 29 | year_month int NOT NULL, 30 | total_price numeric(12, 2) default 0, 31 | total_rely_bulk_price numeric(12, 2) default 0, 32 | default_badge varchar(25), 33 | monthly_badge varchar(25), 34 | status varchar(15), 35 | created_date timestamptz, 36 | last_modified_date timestamptz 37 | ); 38 | CREATE UNIQUE INDEX idx_customer_badge_customer_id_year_month ON customer_badge (customer_id, year_month); 39 | 40 | -------------------------------------------------------------------------------- /src/test/java/com/sellde/utility/CalculateBadgeTest.java: -------------------------------------------------------------------------------- 1 | package com.sellde.utility; 2 | 3 | import static com.sellde.reward.enums.CustomerMonthlyBadge.*; 4 | 5 | import com.sellde.reward.enums.CustomerMonthlyBadge; 6 | import com.sellde.reward.util.Utils; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class CalculateBadgeTest { 10 | 11 | @Test 12 | void test() { 13 | System.out.println("Testing"); 14 | } 15 | 16 | void calculateBadge(CustomerMonthlyBadge thisMonth, CustomerMonthlyBadge lastMonth, CustomerMonthlyBadge last2ndMonth) { 17 | 18 | if (lastMonth == null && last2ndMonth == null) { 19 | 20 | } 21 | 22 | } 23 | 24 | void calculateWhenLast2Month(CustomerMonthlyBadge lastMonth, CustomerMonthlyBadge last2ndMonth) { 25 | var thisMonth = NA; 26 | var thisMonthValue = NA.value; 27 | var last2ndMonthValue = last2ndMonth.value; 28 | var lastMonthValue = lastMonth.value; 29 | 30 | 31 | if (last2ndMonthValue == lastMonthValue) { 32 | thisMonthValue = lastMonthValue; 33 | } 34 | if (last2ndMonthValue > lastMonthValue) { 35 | thisMonthValue = last2ndMonthValue - 1; 36 | } 37 | if (last2ndMonthValue < lastMonthValue) { 38 | thisMonthValue = lastMonthValue ; 39 | } 40 | 41 | if (last2ndMonth == SILVER) { 42 | if (lastMonth == SILVER) { 43 | thisMonth = SILVER; 44 | } else if (lastMonth == BRONZE) { 45 | thisMonth = BRONZE; 46 | } else { 47 | thisMonth = BRONZE; 48 | } 49 | } 50 | if (last2ndMonth == BRONZE) { 51 | 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/enums/CustomerMonthlyBadge.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.enums; 2 | 3 | public enum CustomerMonthlyBadge { 4 | 5 | NA(0), 6 | BRONZE(2000000), 7 | SILVER(5000000); 8 | 9 | /* NA, BRONZE, SILVER (0,1,2) */ 10 | public static final int badgeLevel = 2; 11 | 12 | private static final int NA_VALUE = 0; 13 | private static final int BRONZE_VALUE = 1; 14 | private static final int SILVER_VALUE = 2; 15 | 16 | 17 | public final long value; 18 | 19 | CustomerMonthlyBadge(long value) { 20 | this.value = value; 21 | } 22 | 23 | public static int getIndex(CustomerMonthlyBadge badge) { 24 | return switch (badge) { 25 | case BRONZE -> BRONZE_VALUE; 26 | case SILVER -> SILVER_VALUE; 27 | default -> NA_VALUE; 28 | }; 29 | } 30 | 31 | public static int compare(CustomerMonthlyBadge previousBadge, 32 | CustomerMonthlyBadge currentBadge) { 33 | var previousIndex = getIndex(previousBadge); 34 | var currentIndex = getIndex(currentBadge); 35 | return previousIndex - currentIndex; 36 | } 37 | 38 | public static CustomerMonthlyBadge getValueByIndex(int index) { 39 | return switch (index) { 40 | case BRONZE_VALUE -> BRONZE; 41 | case SILVER_VALUE -> SILVER; 42 | default -> NA; 43 | }; 44 | } 45 | 46 | public static CustomerMonthlyBadge getValueByMinusIndex(CustomerMonthlyBadge badge, int minusIndex) { 47 | var badgeIndex = getIndex(badge) - minusIndex; 48 | return switch (badgeIndex) { 49 | case BRONZE_VALUE -> BRONZE; 50 | case SILVER_VALUE -> SILVER; 51 | default -> NA; 52 | }; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/CustomerBadgeModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import com.sellde.reward.util.Constant; 5 | import lombok.AllArgsConstructor; 6 | import lombok.Data; 7 | import lombok.With; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | import java.util.UUID; 13 | 14 | @Data 15 | @With 16 | @AllArgsConstructor 17 | public class CustomerBadgeModel { 18 | 19 | private UUID id; 20 | private UUID customerId; 21 | private Integer yearMonth; 22 | private BigDecimal totalPrice; 23 | private BigDecimal totalShippingPrice; 24 | private BigDecimal totalDiscountPrice; 25 | private BigDecimal totalRelyBulkPrice; 26 | private String defaultBadge; 27 | private String monthlyBadge; 28 | private String note; 29 | private StatusType status; 30 | 31 | /* only for evaluation */ 32 | private String finalBadge; 33 | private List customerBadgeRangeList; 34 | 35 | public CustomerBadgeModel() { 36 | defaultBadge = Constant.NOT_AVIALBLE; 37 | monthlyBadge = Constant.NOT_AVIALBLE; 38 | finalBadge = Constant.NOT_AVIALBLE; 39 | status = StatusType.ENABLE; 40 | totalRelyBulkPrice = BigDecimal.ZERO; 41 | totalPrice = BigDecimal.ZERO; 42 | totalDiscountPrice = BigDecimal.ZERO; 43 | totalShippingPrice = BigDecimal.ZERO; 44 | customerBadgeRangeList = new ArrayList<>(); 45 | } 46 | 47 | public UUID getCustomerBadgeId() { 48 | return getId(); 49 | } 50 | 51 | /* without shipping & exclude discount */ 52 | public BigDecimal getActualSpentTotalPrice() { 53 | return totalPrice.subtract(totalShippingPrice).add(totalDiscountPrice); 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/MonthlyBadgeBrandConfigService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.repository.MonthlyBadgeBrandConfigRepository; 4 | import com.sellde.reward.service.mapper.MonthlyBadgeConfigMapper; 5 | import com.sellde.reward.service.model.MonthlyBadgeBrandConfigModel; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | import reactor.core.publisher.Flux; 9 | 10 | import java.util.UUID; 11 | 12 | @Service 13 | public class MonthlyBadgeBrandConfigService { 14 | 15 | @Autowired 16 | MonthlyBadgeBrandConfigRepository monthlyBadgeConfigRepository; 17 | 18 | @Autowired 19 | MonthlyBadgeConfigMapper monthlyBadgeConfigMapper; 20 | 21 | public Flux getByMonthlyBadgeId(UUID monthlyBadgeId) { 22 | return monthlyBadgeConfigRepository.findAllByMonthlyBadgeIdOrderByGoodsBrandName(monthlyBadgeId) 23 | .map(monthlyBadgeConfigMapper::toModel); 24 | } 25 | 26 | public Flux create(MonthlyBadgeBrandConfigModel model) { 27 | if (model.getId() != null) { 28 | return update(model); 29 | } 30 | return monthlyBadgeConfigRepository.save(monthlyBadgeConfigMapper.toEntity(model)) 31 | .thenMany(monthlyBadgeConfigRepository.findAllByMonthlyBadgeIdOrderByGoodsBrandName(model.getMonthlyBadgeId())) 32 | .map(monthlyBadgeConfigMapper::toModel); 33 | } 34 | 35 | public Flux update(MonthlyBadgeBrandConfigModel model) { 36 | return monthlyBadgeConfigRepository.findById(model.getId()) 37 | .map(item -> monthlyBadgeConfigMapper.toUpdateEntity(item, model)) 38 | .flatMap(monthlyBadgeConfigRepository::save) 39 | .thenMany(monthlyBadgeConfigRepository.findAllByMonthlyBadgeIdOrderByGoodsBrandName(model.getMonthlyBadgeId())) 40 | .map(monthlyBadgeConfigMapper::toModel); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/open/OpenSupplierSalesResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest.open; 2 | 3 | 4 | import com.sellde.reward.service.biz.SupplierSalesBizService; 5 | import com.sellde.reward.service.model.SupplierSalesModel; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.ResponseEntity; 10 | import org.springframework.web.bind.annotation.*; 11 | import reactor.core.publisher.Mono; 12 | 13 | import static com.sellde.reward.service.model.SupplierSalesModel.SqsMessage; 14 | import static com.sellde.reward.service.model.SupplierSalesModel.SqsMessageResponse; 15 | 16 | @RestController 17 | @RequestMapping("v1/open/supplier-sales") 18 | public class OpenSupplierSalesResource { 19 | 20 | @Autowired 21 | private SupplierSalesBizService supplierSalesBizService; 22 | 23 | @Value("${sellde.supply-reward-api-key}") 24 | private String supplyRewardApiKey; 25 | 26 | @PostMapping() 27 | public Mono> createOrUpdateBasket( 28 | @RequestHeader(required = false, value = "supply-reward-api-key") String apiKey, 29 | @RequestBody SqsMessage sqsMessage) { 30 | if (supplyRewardApiKey.equalsIgnoreCase(apiKey)) { 31 | return supplierSalesBizService. 32 | processBasketAndUpdateCustomerBadge(sqsMessage.getRequest()) 33 | .map(item -> new SupplierSalesModel.SqsMessageResponse() 34 | .withMessage("successfully done") 35 | .withStatus("200")) 36 | .map(item -> ResponseEntity.status(HttpStatus.OK).body(item)); 37 | } else { 38 | var response = new SqsMessageResponse() 39 | .withMessage("invalid key") 40 | .withStatus("401"); 41 | return Mono.just(ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response)); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/SignupRewardResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | import com.sellde.reward.service.SettingService; 4 | import com.sellde.reward.service.SignupRewardService; 5 | import com.sellde.reward.service.model.SignupRewardModel; 6 | import com.sellde.reward.util.Constant; 7 | import org.json.JSONObject; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.http.HttpStatus; 10 | import org.springframework.web.bind.annotation.*; 11 | import reactor.core.publisher.Flux; 12 | import reactor.core.publisher.Mono; 13 | 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | 18 | @RestController 19 | @RequestMapping("v1/signup-reward") 20 | public class SignupRewardResource { 21 | 22 | @Autowired 23 | private SignupRewardService signupRewardService; 24 | 25 | @Autowired 26 | private SettingService settingService; 27 | 28 | @GetMapping 29 | public Flux getAll() { 30 | return signupRewardService.getAll(); 31 | } 32 | 33 | @PostMapping() 34 | public Flux create(@RequestBody List modelList) { 35 | return signupRewardService.createOrUpdate(modelList); 36 | } 37 | 38 | @PutMapping("/status") 39 | @ResponseStatus(HttpStatus.OK) 40 | public Mono updateRewardStatus(@RequestParam Boolean isStoreSignupDiscountPromoOn) { 41 | return settingService.updateStoreSignupDiscountPromoOn(isStoreSignupDiscountPromoOn) 42 | .map(flag -> new JSONObject() 43 | .put(Constant.SIGNUP_PROMO_CONFIG, flag) 44 | .toMap() 45 | ); 46 | } 47 | 48 | @GetMapping("/status") 49 | @ResponseStatus(HttpStatus.OK) 50 | public Mono getRewardStatus() { 51 | return settingService.isStoreSignupDiscountPromoOn() 52 | .map(flag -> new JSONObject() 53 | .put(Constant.SIGNUP_PROMO_CONFIG, flag) 54 | .toMap() 55 | ); 56 | } 57 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/R2dbcConfig.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.sellde.reward.config.support.JsonToMapConverter; 5 | import com.sellde.reward.config.support.MapToJsonConverter; 6 | import io.r2dbc.spi.ConnectionFactory; 7 | import org.springframework.beans.factory.annotation.Qualifier; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.core.convert.converter.Converter; 11 | import org.springframework.data.r2dbc.config.AbstractR2dbcConfiguration; 12 | import org.springframework.data.r2dbc.config.EnableR2dbcAuditing; 13 | import org.springframework.data.r2dbc.convert.R2dbcCustomConversions; 14 | import org.springframework.transaction.annotation.EnableTransactionManagement; 15 | 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | 19 | @Configuration 20 | @EnableR2dbcAuditing 21 | @EnableTransactionManagement 22 | public class R2dbcConfig extends AbstractR2dbcConfiguration { 23 | 24 | private final ObjectMapper objectMapper; 25 | 26 | private final ConnectionFactory connectionFactory; 27 | 28 | 29 | public R2dbcConfig(@Qualifier("connectionFactory") ConnectionFactory connectionFactory, 30 | ObjectMapper objectMapper){ 31 | this.connectionFactory=connectionFactory; 32 | this.objectMapper=objectMapper; 33 | } 34 | 35 | /** 36 | * Adding converters for Json To Hashmap database column 37 | * 38 | * @return 39 | */ 40 | @Bean 41 | @Override 42 | public R2dbcCustomConversions r2dbcCustomConversions() { 43 | List> converters = new ArrayList<>(); 44 | converters.add(new JsonToMapConverter(objectMapper)); 45 | converters.add(new MapToJsonConverter(objectMapper)); 46 | return new R2dbcCustomConversions(getStoreConversions(), converters); 47 | } 48 | 49 | @Override 50 | public ConnectionFactory connectionFactory() { 51 | return connectionFactory; 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/MonthlyBadgeResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | 4 | import com.sellde.reward.service.MonthlyBadgeBrandConfigService; 5 | import com.sellde.reward.service.MonthlyBadgeService; 6 | import com.sellde.reward.service.model.MonthlyBadgeBrandConfigModel; 7 | import com.sellde.reward.service.model.MonthlyBadgeModel; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.web.bind.annotation.*; 10 | import reactor.core.publisher.Flux; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.UUID; 14 | 15 | @RestController 16 | @RequestMapping("v1/monthly-badge") 17 | public class MonthlyBadgeResource { 18 | 19 | @Autowired 20 | private MonthlyBadgeService monthlyBadgeService; 21 | 22 | @Autowired 23 | private MonthlyBadgeBrandConfigService monthlyBadgeConfigService; 24 | 25 | @GetMapping() 26 | public Flux getAll() { 27 | return monthlyBadgeService.getAll(); 28 | } 29 | 30 | @PostMapping() 31 | public Mono create(@RequestBody MonthlyBadgeModel model) { 32 | return monthlyBadgeService.create(model); 33 | } 34 | 35 | @PutMapping("/{monthlyBadgeId}") 36 | public Mono update(@PathVariable UUID monthlyBadgeId, @RequestBody MonthlyBadgeModel model) { 37 | model.setId(monthlyBadgeId); 38 | return monthlyBadgeService.update(model); 39 | } 40 | 41 | @GetMapping("/{monthlyBadgeId}/config") 42 | public Flux getBrandConfig(@PathVariable UUID monthlyBadgeId) { 43 | return monthlyBadgeConfigService.getByMonthlyBadgeId(monthlyBadgeId); 44 | } 45 | 46 | @PostMapping("/{monthlyBadgeId}/config") 47 | public Flux createBrandConfig(@PathVariable UUID monthlyBadgeId, 48 | @RequestBody MonthlyBadgeBrandConfigModel model) { 49 | model.setMonthlyBadgeId(monthlyBadgeId); 50 | return monthlyBadgeConfigService.create(model); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/config/CustomWebFluxConfig.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.config; 2 | 3 | import com.sellde.reward.util.Constant; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.context.annotation.Configuration; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.format.FormatterRegistry; 8 | import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; 9 | import org.springframework.http.HttpMethod; 10 | import org.springframework.web.reactive.config.CorsRegistry; 11 | import org.springframework.web.reactive.config.WebFluxConfigurer; 12 | 13 | @Configuration 14 | @Slf4j 15 | public class CustomWebFluxConfig implements WebFluxConfigurer { 16 | 17 | private final Environment env; 18 | 19 | public CustomWebFluxConfig(Environment env) { 20 | this.env = env; 21 | addDefaultActiveProfile(); 22 | } 23 | 24 | /** 25 | * Cors setting 26 | * 27 | * @param corsRegistry 28 | */ 29 | @Override 30 | public void addCorsMappings(CorsRegistry corsRegistry) { 31 | corsRegistry.addMapping("/**") 32 | .allowedOrigins("*") 33 | .allowedMethods(HttpMethod.GET.name(),HttpMethod.POST.name(),HttpMethod.PUT.name(),HttpMethod.DELETE.name()) 34 | .maxAge(3600); 35 | } 36 | 37 | /** 38 | * Set datetime to ISO format 39 | * 40 | * @param registry 41 | */ 42 | @Override 43 | public void addFormatters(FormatterRegistry registry) { 44 | var registrar = new DateTimeFormatterRegistrar(); 45 | registrar.setUseIsoFormat(true); 46 | registrar.registerFormatters(registry); 47 | } 48 | 49 | /** 50 | * Set default profile to Local when no profile defined to 51 | */ 52 | public void addDefaultActiveProfile() { 53 | var activeProfiles = env.getActiveProfiles(); 54 | if (activeProfiles.length < 1) { 55 | log.info("Application is running with default profile : local"); 56 | System.setProperty(Constant.ACTIVE_PROFILE, Constant.ENV_LOCAL); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/ShippingFeePromoResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | import com.sellde.reward.service.SettingService; 4 | import com.sellde.reward.service.ShippingPromoService; 5 | import com.sellde.reward.service.model.ShippingFeePromoModel; 6 | import org.json.JSONObject; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.web.bind.annotation.*; 10 | import reactor.core.publisher.Flux; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.List; 14 | import java.util.Map; 15 | 16 | @RestController 17 | @RequestMapping("v1/shipping-fee-promo") 18 | public class ShippingFeePromoResource { 19 | 20 | @Autowired 21 | private SettingService settingService; 22 | @Autowired 23 | private ShippingPromoService shippingPromoService; 24 | 25 | @PutMapping("/status") 26 | @ResponseStatus(HttpStatus.OK) 27 | public Mono updatePromoStatus(@RequestParam Boolean isShippingFeePromoOn) { 28 | return settingService.updateShippingFeePromo(isShippingFeePromoOn) 29 | .map(flag -> new JSONObject() 30 | .put("isShippingFeePromoOn", flag) 31 | .toMap() 32 | ); 33 | } 34 | 35 | @GetMapping("/status") 36 | @ResponseStatus(HttpStatus.OK) 37 | public Mono getPromoStatus() { 38 | return settingService.isShippingFeePromoOn() 39 | .map(flag -> new JSONObject() 40 | .put("isShippingFeePromoOn", flag) 41 | .toMap() 42 | ); 43 | } 44 | 45 | @PostMapping() 46 | @ResponseStatus(HttpStatus.OK) 47 | public Flux crateOrUpdate(@RequestBody List shippingFeePromoModelList) { 48 | return shippingPromoService.create(shippingFeePromoModelList); 49 | } 50 | 51 | 52 | @GetMapping() 53 | @ResponseStatus(HttpStatus.OK) 54 | public Flux getAll() { 55 | return shippingPromoService.getAllPromos(); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/SupplierSalesService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.repository.BasketSalesCustomRepository; 4 | import com.sellde.reward.repository.SupplierSalesRepository; 5 | import com.sellde.reward.service.mapper.SupplierSalesMapper; 6 | import com.sellde.reward.service.model.SupplierSalesModel; 7 | import lombok.AccessLevel; 8 | import lombok.experimental.FieldDefaults; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.UUID; 14 | 15 | @Service 16 | @FieldDefaults(level = AccessLevel.PRIVATE) 17 | public class SupplierSalesService { 18 | 19 | @Autowired 20 | SupplierSalesRepository supplierSalesRepository; 21 | 22 | @Autowired 23 | BasketSalesCustomRepository basketSalesCustomRepository; 24 | 25 | @Autowired 26 | SupplierSalesMapper supplierSalesMapper; 27 | 28 | public Mono createFromBasket(SupplierSalesModel.BasketRequest request) { 29 | return supplierSalesRepository 30 | .findByOrderNo(request.getOrderNo()) 31 | .map(existingItem -> supplierSalesMapper.toUpdateEntity(existingItem, request)) 32 | .defaultIfEmpty(supplierSalesMapper.toEntityFromBasket(request)) 33 | .flatMap(supplierSalesRepository::save) 34 | .map(supplierSalesMapper::toBasketResponse); 35 | } 36 | 37 | public Mono getMaxOrderNoForCustomer(UUID customerId) { 38 | return supplierSalesRepository.findMaxValidOrderForCustomer(customerId) 39 | .map(supplierSalesMapper::toBasketResponse); 40 | } 41 | 42 | public Mono getSumOfBasketSalesByCustomer(SupplierSalesModel.BasketRequest request) { 43 | var year = request.getOrderDate().getYear(); 44 | var month = request.getOrderDate().getMonth().getValue(); 45 | return basketSalesCustomRepository 46 | .getSumOfSalesOfCustomer(request.getCustomerId(), year, month); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/util/Utils.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.util; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | 5 | import java.time.LocalDate; 6 | import java.time.ZoneId; 7 | import java.util.Collection; 8 | import java.util.Objects; 9 | 10 | public class Utils { 11 | 12 | private static ObjectMapper objectMapper = new ObjectMapper(); 13 | 14 | public static String cleanMobileNo(String mobileNo) { 15 | if (mobileNo.contains("+")) { 16 | mobileNo = mobileNo.substring(1); 17 | } 18 | if (mobileNo.startsWith("84")) { 19 | mobileNo = mobileNo.substring(2); 20 | mobileNo = "0" + mobileNo; 21 | } 22 | var mobileNoAsInt = Long.parseLong(mobileNo); 23 | return "0" + mobileNoAsInt; 24 | } 25 | 26 | public static boolean isNullOrEmpty(Collection list) { 27 | if (list == null || list.isEmpty()) { 28 | return true; 29 | } 30 | return false; 31 | } 32 | 33 | public static boolean isNotEmpty(Collection list) { 34 | return !isNullOrEmpty(list); 35 | } 36 | 37 | public static boolean isNotNull(Object obj) { 38 | return !Objects.isNull(obj); 39 | } 40 | 41 | public static LocalDate currentLocalDate() { 42 | return LocalDate.now(ZoneId.of("Asia/Ho_Chi_Minh")); 43 | } 44 | 45 | public static String emptyString() { 46 | return ""; 47 | } 48 | 49 | public static int todayYearMonth() { 50 | var today = LocalDate.now(); 51 | var currentYear = today.getYear(); 52 | var month = today.getMonth().getValue(); 53 | return (currentYear * 100) + month; 54 | } 55 | 56 | public static int todayYearMonth(LocalDate localDate) { 57 | var currentYear = localDate.getYear(); 58 | var month = localDate.getMonth().getValue(); 59 | return (currentYear * 100) + month; 60 | } 61 | 62 | public static String printAsJson(Object object) { 63 | try { 64 | return objectMapper.writeValueAsString(object); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | return null; 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/security/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.security; 2 | 3 | import com.sellde.reward.security.cognito.AwsCognitoJwtAuthFilter; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.context.annotation.Bean; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.http.HttpMethod; 8 | import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; 9 | import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity; 10 | import org.springframework.security.config.web.server.SecurityWebFiltersOrder; 11 | import org.springframework.security.config.web.server.ServerHttpSecurity; 12 | import org.springframework.security.web.server.SecurityWebFilterChain; 13 | 14 | import static com.sellde.reward.util.Constant.ACTIVE_PROFILE; 15 | 16 | @EnableWebFluxSecurity 17 | @EnableReactiveMethodSecurity 18 | public class SecurityConfiguration { 19 | 20 | @Autowired 21 | private Environment environment; 22 | 23 | 24 | @Bean 25 | public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) { 26 | ServerHttpSecurity.AuthorizeExchangeSpec 27 | authExchangeSpec = http.requestCache().disable(). 28 | csrf().disable() 29 | .httpBasic(ServerHttpSecurity.HttpBasicSpec::disable) 30 | .addFilterAt(new AwsCognitoJwtAuthFilter(), SecurityWebFiltersOrder.HTTP_BASIC) 31 | .authorizeExchange(); 32 | 33 | String activeProfile = null; 34 | if (environment.getActiveProfiles().length > 0) { 35 | activeProfile = environment.getActiveProfiles()[0]; 36 | } 37 | if (activeProfile == null) { 38 | activeProfile = environment.getProperty(ACTIVE_PROFILE); 39 | } 40 | 41 | authExchangeSpec 42 | .pathMatchers(HttpMethod.OPTIONS, "/v1/**").permitAll() 43 | .pathMatchers("/v1/**").authenticated() 44 | // swagger related, in prod we can block it 45 | .pathMatchers(HttpMethod.GET, "/**").permitAll(); 46 | ; 47 | return http.build(); 48 | } 49 | } -------------------------------------------------------------------------------- /src/test/java/com/sellde/inte/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package com.sellde.inte; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.sellde.reward.SupplyRewardEngineApplication; 5 | import com.sellde.reward.repository.CustomerBadgeRepository; 6 | import com.sellde.reward.repository.SupplierSalesRepository; 7 | import com.sellde.reward.repository.UserProfileCopyRepository; 8 | import com.sellde.reward.util.Constant; 9 | import org.junit.jupiter.api.extension.ExtendWith; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.boot.test.context.SpringBootTest; 12 | import org.springframework.test.context.ActiveProfiles; 13 | import org.springframework.test.context.TestPropertySource; 14 | import org.springframework.test.context.junit.jupiter.SpringExtension; 15 | import org.springframework.test.web.reactive.server.WebTestClient; 16 | 17 | /** 18 | * Base composite annotation for integration tests. 19 | */ 20 | 21 | @ExtendWith(SpringExtension.class) 22 | @SpringBootTest(classes = SupplyRewardEngineApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) 23 | @ActiveProfiles(Constant.ENV_TEST) 24 | @TestPropertySource("classpath:config/application-test.yml") 25 | public abstract class IntegrationTest { 26 | 27 | @Autowired 28 | SupplierSalesRepository supplierSalesRepository; 29 | 30 | @Autowired 31 | UserProfileCopyRepository userProfileCopyRepository; 32 | 33 | @Autowired 34 | CustomerBadgeRepository customerBadgeRepository; 35 | 36 | @Autowired 37 | protected WebTestClient webTestClient; 38 | 39 | @Autowired 40 | protected ObjectMapper objectMapper; 41 | 42 | protected void cleanDB() { 43 | customerBadgeRepository.deleteAll() 44 | .then(supplierSalesRepository.deleteAll()) 45 | .then(customerBadgeRepository.deleteAll()) 46 | .block(); 47 | } 48 | 49 | protected String printAsJson(Object object) { 50 | try { 51 | System.out.println("data -> " + object); 52 | return objectMapper.writeValueAsString(object); 53 | } catch (Exception e) { 54 | e.printStackTrace(); 55 | return null; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/SupplyRewardEngineApplication.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | import org.springframework.core.env.Environment; 7 | import org.springframework.util.StringUtils; 8 | 9 | import java.net.InetAddress; 10 | import java.net.UnknownHostException; 11 | import java.util.Optional; 12 | 13 | @Slf4j 14 | @SpringBootApplication 15 | public class SupplyRewardEngineApplication { 16 | 17 | public static void main(String[] args) { 18 | var app = new SpringApplication(SupplyRewardEngineApplication.class); 19 | Environment env = app.run(args).getEnvironment(); 20 | logApplicationStartup(env); 21 | } 22 | 23 | private static void logApplicationStartup(Environment env) { 24 | var protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")) 25 | .map(key -> "https").orElse("http"); 26 | var serverPort = env.getProperty("server.port"); 27 | var contextPath = (env.getProperty("spring.webflux.base-path")+"/"); 28 | var hostAddress = "localhost"; 29 | try { 30 | hostAddress = InetAddress.getLocalHost().getHostAddress(); 31 | } catch (UnknownHostException e) { 32 | log.warn("The host name could not be determined, using `localhost` as fallback"); 33 | } 34 | log.info(""" 35 | 36 | ---------------------------------------------------------- 37 | Application '{}' is running! Access URLs: 38 | Local: {}://localhost:{}{}swagger-ui.html 39 | External: {}://{}:{}{} 40 | Profile(s): {} 41 | ---------------------------------------------------------- 42 | """, 43 | env.getProperty("spring.application.name"), 44 | protocol, serverPort, contextPath, 45 | protocol, hostAddress, serverPort, contextPath, 46 | env.getActiveProfiles() 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/CouponTrackerService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import com.sellde.reward.repository.CouponTrackerRepository; 5 | import com.sellde.reward.service.mapper.CouponTrackerMapper; 6 | import com.sellde.reward.service.model.CouponTrackerModel; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.util.UUID; 12 | 13 | @Service 14 | public class CouponTrackerService { 15 | 16 | @Autowired 17 | private CouponTrackerMapper couponTrackerMapper; 18 | 19 | @Autowired 20 | private DiscountCouponService discountCouponService; 21 | 22 | @Autowired 23 | private CouponTrackerRepository couponTrackerRepository; 24 | 25 | public Mono createUsingCouponName(CouponTrackerModel model) { 26 | // get coupon id by coupon Name 27 | return discountCouponService.getByCouponName(model.getCouponName()) 28 | .map(item -> { 29 | // add coupon id 30 | var domain = couponTrackerMapper.toEntity(model); 31 | domain.setDiscountCouponId(item.getId()); 32 | return domain; 33 | }) 34 | .flatMap(couponTrackerRepository::save) 35 | .map(couponTrackerMapper::toModel) 36 | .doOnNext(savedModel -> savedModel.setCouponName(model.getCouponName())); 37 | } 38 | 39 | public Mono canCustomerApplyCoupon(UUID customerId, String couponName) { 40 | return discountCouponService.getByCouponName(couponName) 41 | .zipWhen(couponModel -> couponTrackerRepository 42 | .countCustomerAppliedCouponWithStatus(couponModel.getId(), customerId, StatusType.ENABLE)) 43 | .map(tuple -> { 44 | var allowedCount = tuple.getT1().getCustomerCount(); 45 | var actualCount = tuple.getT2(); 46 | if (allowedCount > actualCount) { 47 | return true; 48 | } 49 | return false; 50 | }); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/DiscountCouponService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import com.sellde.reward.repository.DiscountCouponRepository; 5 | import com.sellde.reward.service.mapper.DiscountCouponMapper; 6 | import com.sellde.reward.service.model.DiscountCouponModel; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.util.List; 13 | 14 | @Service 15 | public class DiscountCouponService { 16 | 17 | @Autowired 18 | private DiscountCouponRepository discountCouponRepository; 19 | 20 | @Autowired 21 | private DiscountCouponMapper discountCouponMapper; 22 | 23 | 24 | public Flux getAllForOps() { 25 | return discountCouponRepository.findAll() 26 | .map(item -> discountCouponMapper.toModel(item)); 27 | } 28 | 29 | public Mono createOrUpdate(DiscountCouponModel model) { 30 | return discountCouponRepository 31 | .save(discountCouponMapper.toEntity(model)) 32 | .map(item -> discountCouponMapper.toModel(item)); 33 | } 34 | 35 | public Flux createOrUpdate(List modelList) { 36 | var domainList = discountCouponMapper.toEntity(modelList); 37 | return discountCouponRepository 38 | .saveAll(domainList) 39 | .map(item -> discountCouponMapper.toModel(item)); 40 | } 41 | 42 | public Flux getAllByEnableStatus() { 43 | return discountCouponRepository.findAllByStatus(StatusType.ENABLE) 44 | .map(discountCouponMapper::toModel); 45 | } 46 | 47 | public Mono getByCouponName(String couponName) { 48 | return discountCouponRepository.findByCouponName(couponName) 49 | .map(discountCouponMapper::toModel); 50 | } 51 | 52 | public Flux getByCouponNameList(List couponNameList) { 53 | return discountCouponRepository.findByCouponNameIsIn(couponNameList,StatusType.ENABLE) 54 | .map(discountCouponMapper::toModel); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/com/sellde/inte/CustomerBadgeResourceTest.java: -------------------------------------------------------------------------------- 1 | package com.sellde.inte; 2 | 3 | import com.sellde.reward.service.CustomerBadgeService; 4 | import com.sellde.reward.service.biz.SupplierSalesBizService; 5 | import com.sellde.reward.service.model.CustomerBadgeModel; 6 | import com.sellde.utility.DomainUtil; 7 | import org.junit.jupiter.api.Test; 8 | import org.junit.jupiter.api.TestInstance; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.http.MediaType; 11 | 12 | import java.time.LocalDate; 13 | 14 | 15 | @TestInstance(TestInstance.Lifecycle.PER_CLASS) 16 | class CustomerBadgeResourceTest extends IntegrationTest { 17 | 18 | @Autowired 19 | CustomerBadgeService customerBadgeService; 20 | 21 | @Autowired 22 | SupplierSalesBizService supplierSalesBizService; 23 | 24 | @Test 25 | void testGetByParam() { 26 | 27 | cleanDB(); 28 | 29 | var basketRequest = DomainUtil.newBasketRequestModel() 30 | /* set today order date */ 31 | .withOrderDate(LocalDate.now()); 32 | 33 | supplierSalesBizService.processBasketAndUpdateCustomerBadge(basketRequest) 34 | .block(); 35 | 36 | var customerId = basketRequest.getCustomerId(); 37 | 38 | var responseModel = webTestClient 39 | .get() 40 | .uri("/v1/customer-badge?customerId={1}", customerId) 41 | .exchange() 42 | .expectStatus().isOk() 43 | .expectHeader().valueEquals("Content-Type", MediaType.APPLICATION_JSON_VALUE) 44 | .expectBody(CustomerBadgeModel.class) 45 | .returnResult().getResponseBody(); 46 | System.out.println("Response -> " + printAsJson(responseModel)); 47 | 48 | assert responseModel != null; 49 | assert responseModel.getFinalBadge().equals("SILVER"); 50 | assert responseModel.getTotalPrice().longValue() == 4500000L; 51 | assert responseModel.getTotalDiscountPrice().longValue() == 40000L; 52 | assert responseModel.getTotalShippingPrice().longValue() == 15000L; 53 | assert responseModel.getActualSpentTotalPrice().longValue() == 4525000L; 54 | assert responseModel.getFinalBadge().equals("SILVER"); 55 | assert responseModel.getCustomerBadgeRangeList().size() != 0; 56 | assert responseModel.getCustomerId().toString().equals(basketRequest.getCustomerId().toString()); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/repository/BasketSalesCustomRepository.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.repository; 2 | 3 | import com.sellde.reward.service.model.SupplierSalesModel; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.r2dbc.core.DatabaseClient; 6 | import org.springframework.stereotype.Repository; 7 | import reactor.core.publisher.Mono; 8 | 9 | import java.math.BigDecimal; 10 | import java.util.UUID; 11 | 12 | 13 | @Repository 14 | public class BasketSalesCustomRepository { 15 | 16 | @Autowired 17 | private DatabaseClient databaseClient; 18 | 19 | public Mono getSumOfSalesOfCustomer(UUID customerId, int salesYear, int salesMonth) { 20 | var sql = """ 21 | select coalesce(sum(ss.total_price), 0) as total_price, 22 | coalesce(sum(ss.total_rely_bulk_price) ,0 )as total_rely_bulk_price, 23 | coalesce(sum(ss.discount_price), 0) as total_discount_price, 24 | coalesce(sum(ss.shipping_price), 0) as total_shipping_price 25 | from basket_sales ss 26 | where ss.customer_id = :customerId 27 | and EXTRACT(YEAR FROM ss.order_date) = :salesYear 28 | and EXTRACT(MONTH FROM ss.order_date) = :salesMonth 29 | and ss.basket_status not in ('ORDER_PLACED','ORDER_CANCEL','ORDER_REJECT','ORDER_INVALID') 30 | """; 31 | 32 | return databaseClient 33 | .sql(sql) 34 | .bind("customerId", customerId) 35 | .bind("salesYear", salesYear) 36 | .bind("salesMonth", salesMonth) 37 | .map((row, rowMetaData) -> 38 | new SupplierSalesModel.SumOfBasketSales() 39 | .withCustomerId(customerId) 40 | .withTotalPrice(row.get("total_price", BigDecimal.class)) 41 | .withTotalDiscountPrice(row.get("total_discount_price", BigDecimal.class)) 42 | .withTotalShippingPrice(row.get("total_shipping_price", BigDecimal.class)) 43 | .withTotalRelyBulkPrice(row.get("total_rely_bulk_price", BigDecimal.class)) 44 | .withYearMonth(Integer.getInteger(salesYear + "" + salesMonth)) 45 | ) 46 | .first() 47 | ; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/rest/DiscountCouponResource.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.rest; 2 | 3 | import com.sellde.reward.service.DiscountCouponService; 4 | import com.sellde.reward.service.SettingService; 5 | import com.sellde.reward.service.model.DiscountCouponModel; 6 | import com.sellde.reward.util.Constant; 7 | 8 | import static com.sellde.reward.util.Utils.isNotEmpty; 9 | 10 | import lombok.AccessLevel; 11 | import lombok.experimental.FieldDefaults; 12 | import org.json.JSONObject; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.http.HttpStatus; 15 | import org.springframework.web.bind.annotation.*; 16 | import reactor.core.publisher.Flux; 17 | import reactor.core.publisher.Mono; 18 | 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | @RestController 23 | @RequestMapping("v1/discount-coupon") 24 | @FieldDefaults(level = AccessLevel.PRIVATE) 25 | public class DiscountCouponResource { 26 | 27 | @Autowired 28 | DiscountCouponService discountCouponService; 29 | 30 | @Autowired 31 | SettingService settingService; 32 | 33 | @GetMapping() 34 | @ResponseStatus(HttpStatus.OK) 35 | public Flux getByParam(@RequestParam(required = false) List couponNameList) { 36 | if (isNotEmpty(couponNameList)) { 37 | return discountCouponService.getByCouponNameList(couponNameList); 38 | } 39 | return discountCouponService.getAllForOps(); 40 | } 41 | 42 | @PostMapping() 43 | public Flux createOrUpdate(@RequestBody List modelList) { 44 | return discountCouponService.createOrUpdate(modelList); 45 | } 46 | 47 | @PutMapping("/status") 48 | @ResponseStatus(HttpStatus.OK) 49 | public Mono updateDiscountCouponStatus(@RequestParam Boolean isStoreDiscountCouponPromoOn) { 50 | return settingService.updateStoreDiscountCouponPromoOn(isStoreDiscountCouponPromoOn) 51 | .map(flag -> new JSONObject() 52 | .put(Constant.DISCOUNT_COUPON_CONFIG, flag) 53 | .toMap() 54 | ); 55 | } 56 | 57 | @GetMapping("/status") 58 | @ResponseStatus(HttpStatus.OK) 59 | public Mono getDiscountCouponStatus() { 60 | return settingService.isStoreDiscountCouponPromoOn() 61 | .map(flag -> new JSONObject() 62 | .put(Constant.DISCOUNT_COUPON_CONFIG, flag) 63 | .toMap() 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/UserProfileCopyService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.domain.UserProfileCopy; 4 | import com.sellde.reward.repository.UserProfileCopyRepository; 5 | import com.sellde.reward.service.mapper.UserProfileCopyMapper; 6 | import com.sellde.reward.service.model.UserModel; 7 | import com.sellde.reward.service.userproduct.UserProductHttpClient; 8 | import com.sellde.reward.util.ApiPath; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.util.Objects; 14 | import java.util.UUID; 15 | 16 | @Service 17 | public class UserProfileCopyService { 18 | 19 | @Autowired 20 | private UserProfileCopyRepository userProfileCopyRepository; 21 | 22 | @Autowired 23 | private UserProductHttpClient userHttpclient; 24 | 25 | @Autowired 26 | private UserProfileCopyMapper userProfileCopyMapper; 27 | 28 | public Mono getByUserId(UUID userId) { 29 | return userProfileCopyRepository.findOneByUserId(userId) 30 | .map(userProfileCopyMapper::toModel) 31 | .defaultIfEmpty(new UserModel.ProfileModel()); 32 | } 33 | 34 | public Mono createFromUserId(UUID customerId, String customerName, long phoneNo) { 35 | return userProfileCopyRepository.findOneByUserId(customerId) 36 | .switchIfEmpty(findUser(customerId, customerName, phoneNo)) 37 | .map(userProfileCopyMapper::toModel); 38 | } 39 | 40 | private Mono findUser(UUID customerId, String customerName, long phoneNo) { 41 | return userHttpclient.get(ApiPath.USER_GET_CUSTOMER, UserModel.Request.class, customerId) 42 | .map(userProfileCopyMapper::toEntity) 43 | .map(domain -> { 44 | if (domain.getPhoneNo() == phoneNo) { 45 | domain.setUserName(customerName); 46 | } 47 | return domain; 48 | }) 49 | .flatMap(userProfileCopyRepository::save) 50 | .onErrorReturn(new UserProfileCopy()); 51 | } 52 | 53 | public Mono isStaff(UUID customerId) { 54 | return userProfileCopyRepository.findOneByUserId(customerId) 55 | .map(item -> { 56 | return Objects.equals(item.getSubgroup(), "STAFF"); 57 | }) 58 | .defaultIfEmpty(false); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/security/cognito/AwsCognitoJwtAuthFilter.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.security.cognito; 2 | 3 | import com.sellde.reward.util.Utils; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Data; 6 | import org.springframework.http.server.reactive.ServerHttpRequest; 7 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 8 | import org.springframework.security.core.GrantedAuthority; 9 | import org.springframework.security.core.context.ReactiveSecurityContextHolder; 10 | import org.springframework.web.server.ServerWebExchange; 11 | import org.springframework.web.server.WebFilter; 12 | import org.springframework.web.server.WebFilterChain; 13 | import reactor.core.publisher.Mono; 14 | 15 | import java.util.ArrayList; 16 | import java.util.Objects; 17 | import java.util.UUID; 18 | 19 | public class AwsCognitoJwtAuthFilter implements WebFilter { 20 | 21 | @Override 22 | public Mono filter(ServerWebExchange exchange, WebFilterChain chain) { 23 | var authHeaders = exchange.getRequest().getHeaders().get("Authorization"); 24 | if (Utils.isNotNull(authHeaders)) { 25 | var authenticationToken = getAuthentication(exchange.getRequest()); 26 | var value = ReactiveSecurityContextHolder.getContext() 27 | .contextWrite(ReactiveSecurityContextHolder.withAuthentication(authenticationToken)) 28 | .map(ctx -> { 29 | return ctx; 30 | }) 31 | .then(chain.filter(exchange)); 32 | return value; 33 | } 34 | return chain.filter(exchange); 35 | } 36 | 37 | private UsernamePasswordAuthenticationToken getAuthentication(ServerHttpRequest request) { 38 | var userToken = Objects.requireNonNull(request.getHeaders().get("Authorization")).get(0); 39 | var userId = UUID.randomUUID().toString(); // temporary created, it can be fetched from jwt or db 40 | var auths = new ArrayList(); 41 | var auth = new CustomGrantedAuthority(); 42 | auths.add(auth); 43 | return new UsernamePasswordAuthenticationToken(new PrincipalUser(userId, userToken), userToken, auths); 44 | } 45 | 46 | @Data 47 | @AllArgsConstructor 48 | static 49 | class PrincipalUser { 50 | private String userName; 51 | private String token; 52 | } 53 | 54 | static class CustomGrantedAuthority implements GrantedAuthority { 55 | 56 | @Override 57 | public String getAuthority() { 58 | return "DEFAULT"; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/biz/SupplierSalesBizService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.biz; 2 | 3 | 4 | import com.sellde.reward.service.CustomerBadgeService; 5 | import com.sellde.reward.service.SupplierSalesService; 6 | import com.sellde.reward.service.UserProfileCopyService; 7 | import com.sellde.reward.service.model.CustomerBadgeModel; 8 | import com.sellde.reward.service.model.SupplierSalesModel; 9 | import com.sellde.reward.util.Utils; 10 | import lombok.AccessLevel; 11 | import lombok.experimental.FieldDefaults; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Service; 14 | import org.springframework.transaction.annotation.Transactional; 15 | import reactor.core.publisher.Mono; 16 | 17 | import java.util.List; 18 | 19 | @Service 20 | @Transactional 21 | @FieldDefaults(level = AccessLevel.PRIVATE) 22 | public class SupplierSalesBizService { 23 | 24 | @Autowired 25 | SupplierSalesService supplierSalesService; 26 | 27 | @Autowired 28 | CustomerBadgeService customerBadgeService; 29 | 30 | @Autowired 31 | UserProfileCopyService userProfileCopyService; 32 | 33 | 34 | public Mono> processBasketAndUpdateCustomerBadge(SupplierSalesModel.BasketRequest request) { 35 | return userProfileCopyService.createFromUserId(request.getCustomerId(), request.getCustomerName(), request.getPhoneNo()) /* create user id in user_copy if it's new */ 36 | /* create or update the basket */ 37 | .then(supplierSalesService.createFromBasket(request)) 38 | /* get total of today for customer */ 39 | .zipWhen(item -> supplierSalesService.getSumOfBasketSalesByCustomer(item)) 40 | .map(tuple -> { 41 | var basketResponse = tuple.getT1(); 42 | var sumOfSales = tuple.getT2(); 43 | var orderDate = basketResponse.getOrderDate(); 44 | return new SupplierSalesModel.CustomerBadgeRequest() 45 | .withCustomerId(request.getCustomerId()) 46 | .withYearMonth(Utils.todayYearMonth(orderDate)) 47 | .withTotalPrice(sumOfSales.getTotalPrice()) 48 | .withTotalRelyBulkPrice(sumOfSales.getTotalRelyBulkPrice()) 49 | .withTotalDiscountPrice(sumOfSales.getTotalDiscountPrice()) 50 | .withTotalShippingPrice(sumOfSales.getTotalShippingPrice()) 51 | .withBasketOrderNo(request.getOrderNo()) 52 | .withPostCustomerBadge(request.getPostCustomerBadge()) 53 | ; 54 | }) 55 | /* update customer badge based on basket PostCustomerBadge */ 56 | .flatMap(customerBadgeService::createOrUpdateWhenBasketSalesUpdate); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/resources/json/basket/saved-basket.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "28880452-6aa2-40f1-895f-4470bcd656db", 3 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 4 | "supplierStoreId": "24e4b614-6864-4444-8b3c-c764c0d42ec3", 5 | "orderNo": 1, 6 | "totalPrice": 3950000, 7 | "shippingPrice": 15000, 8 | "discountPrice": 40000, 9 | "totalMarginPrice": 22250, 10 | "selectOption": "FOR_BUYER", 11 | "paymentOption": "BANK_TRANSFER", 12 | "paymentAccountId": "680f1a81-9470-4354-8978-3cc5537b3a22", 13 | "basketGoodsType": "NO_INVOICE", 14 | "basketStatus": "ORDER_PLACED", 15 | "discountDesc": "SHIPPING_PROMO=0", 16 | "shipping": { 17 | "id": "12b7d873-564f-4d83-956e-f8b047388440", 18 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 19 | "customerName": "Rajesh", 20 | "shippingParty": "GHN", 21 | "userAddressId": "680f1a81-9470-4354-8978-3cc5537b3a22", 22 | "provinceName": "Ho Chi Minh", 23 | "districtName": "D2", 24 | "wardName": "An phu", 25 | "address": "Home", 26 | "phoneNo": "900999898", 27 | "landmark": "", 28 | "notes": "", 29 | "method": "DELIVERY", 30 | "status": "ENABLE" 31 | }, 32 | "basketItems": [ 33 | { 34 | "id": "3342a4d6-376b-4401-9b61-2a06fe8ab9ca", 35 | "internalId": 10055, 36 | "quantity": 1, 37 | "goodsId": "94869137-1918-462c-bbaf-895edb88d411", 38 | "goodsName": "Son bóng Sensual Spicy Nude Gloss - 105 Curious Boy", 39 | "goodsBrandName": "Hera", 40 | "hasInvoice": false, 41 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 42 | "imagePath": "529690f7-6f3b-4bc1-bfd6-ab9c8935953a.jpg", 43 | "defaultWholesalePrice": 372750, 44 | "retailPrice": 395000, 45 | "revisedRetailPrice": null, 46 | "marginPrice": 22250, 47 | "bulkPrice": 0, 48 | "supplierPrice": 355000, 49 | "soldPriceType": "RETAIL", 50 | "soldPrice": 395000, 51 | "totalSoldPrice": 395000, 52 | "isRevisedRetailPrice": true, 53 | "status": "ENABLE" 54 | } 55 | ], 56 | "commissionPay": { 57 | "id": "eb199b81-53d9-4f10-9425-11ea276000d5", 58 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 59 | "amount": 22250, 60 | "paymentAccountId": "9acd9277-28bd-4fc5-ba9b-47cac0ee88f5", 61 | "paymentMode": "BANK_TRANSFER", 62 | "status": "ENABLE", 63 | "bankName": null, 64 | "bankUserName": null, 65 | "bankAccountNo": null 66 | }, 67 | "invoice": { 68 | "id": "18bb9850-d195-4e2f-ad64-ccbb7ae459a3", 69 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 70 | "invoiceType": "COMPANY", 71 | "email": "corporate@gmail.com", 72 | "companyName": "home sell", 73 | "taxCode": "TXN8989", 74 | "address": "1, south hall", 75 | "status": "ENABLE" 76 | }, 77 | "orderDate": "2022-09-04T07:40:47.242815Z", 78 | "supplierBankAccount": null, 79 | "supplierWalletAccount": null, 80 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 81 | "shippingFree": false 82 | } -------------------------------------------------------------------------------- /src/test/resources/json/basket/order-100-basket2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "28880452-6aa2-40f1-895f-4470bcd656db", 3 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 4 | "supplierStoreId": "24e4b614-6864-4444-8b3c-c764c0d42ec3", 5 | "orderNo": 100, 6 | "totalPrice": 3950000, 7 | "shippingPrice": 15000, 8 | "discountPrice": 40000, 9 | "totalMarginPrice": 22250, 10 | "selectOption": "FOR_BUYER", 11 | "paymentOption": "BANK_TRANSFER", 12 | "paymentAccountId": "680f1a81-9470-4354-8978-3cc5537b3a22", 13 | "basketGoodsType": "NO_INVOICE", 14 | "basketStatus": "ORDER_SHIPPED", 15 | "discountDesc": "SHIPPING_PROMO=0", 16 | "preCustomerBadge": "BRONZE", 17 | "postCustomerBadge": "SILVER", 18 | "shipping": { 19 | "id": "12b7d873-564f-4d83-956e-f8b047388440", 20 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 21 | "customerName": "Rajesh", 22 | "shippingParty": "GHN", 23 | "userAddressId": "680f1a81-9470-4354-8978-3cc5537b3a22", 24 | "provinceName": "Ho Chi Minh", 25 | "districtName": "D2", 26 | "wardName": "An phu", 27 | "address": "Home", 28 | "phoneNo": "900999898", 29 | "landmark": "", 30 | "notes": "", 31 | "method": "DELIVERY", 32 | "status": "ENABLE" 33 | }, 34 | "basketItems": [ 35 | { 36 | "id": "3342a4d6-376b-4401-9b61-2a06fe8ab9ca", 37 | "internalId": 10055, 38 | "quantity": 1, 39 | "goodsId": "94869137-1918-462c-bbaf-895edb88d411", 40 | "goodsName": "Son bóng Sensual Spicy Nude Gloss - 105 Curious Boy", 41 | "goodsBrandName": "Hera", 42 | "hasInvoice": false, 43 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 44 | "imagePath": "529690f7-6f3b-4bc1-bfd6-ab9c8935953a.jpg", 45 | "defaultWholesalePrice": 372750, 46 | "retailPrice": 395000, 47 | "revisedRetailPrice": null, 48 | "marginPrice": 22250, 49 | "bulkPrice": 0, 50 | "supplierPrice": 355000, 51 | "soldPriceType": "RETAIL", 52 | "soldPrice": 395000, 53 | "totalSoldPrice": 395000, 54 | "isRevisedRetailPrice": true, 55 | "status": "ENABLE" 56 | } 57 | ], 58 | "commissionPay": { 59 | "id": "eb199b81-53d9-4f10-9425-11ea276000d5", 60 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 61 | "amount": 22250, 62 | "paymentAccountId": "9acd9277-28bd-4fc5-ba9b-47cac0ee88f5", 63 | "paymentMode": "BANK_TRANSFER", 64 | "status": "ENABLE", 65 | "bankName": null, 66 | "bankUserName": null, 67 | "bankAccountNo": null 68 | }, 69 | "invoice": { 70 | "id": "18bb9850-d195-4e2f-ad64-ccbb7ae459a3", 71 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 72 | "invoiceType": "COMPANY", 73 | "email": "corporate@gmail.com", 74 | "companyName": "home sell", 75 | "taxCode": "TXN8989", 76 | "address": "1, south hall", 77 | "status": "ENABLE" 78 | }, 79 | "orderDate": "2022-09-04T07:40:47.242815Z", 80 | "supplierBankAccount": null, 81 | "supplierWalletAccount": null, 82 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 83 | "shippingFree": false 84 | } -------------------------------------------------------------------------------- /src/test/resources/json/basket/order-102-basket.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "28880452-6aa2-40f1-895f-4470bcd656db", 3 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 4 | "supplierStoreId": "24e4b614-6864-4444-8b3c-c764c0d42ec3", 5 | "orderNo": 102, 6 | "totalPrice": 5500000, 7 | "shippingPrice": 15000, 8 | "discountPrice": 40000, 9 | "totalMarginPrice": 22250, 10 | "selectOption": "FOR_BUYER", 11 | "paymentOption": "BANK_TRANSFER", 12 | "paymentAccountId": "680f1a81-9470-4354-8978-3cc5537b3a22", 13 | "basketGoodsType": "NO_INVOICE", 14 | "basketStatus": "ORDER_CONFIRMED", 15 | "discountDesc": "SHIPPING_PROMO=0", 16 | "preCustomerBadge": "BRONZE", 17 | "postCustomerBadge": "SILVER", 18 | "shipping": { 19 | "id": "12b7d873-564f-4d83-956e-f8b047388440", 20 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 21 | "customerName": "Rajesh", 22 | "shippingParty": "GHN", 23 | "userAddressId": "680f1a81-9470-4354-8978-3cc5537b3a22", 24 | "provinceName": "Ho Chi Minh", 25 | "districtName": "D2", 26 | "wardName": "An phu", 27 | "address": "Home", 28 | "phoneNo": "900999898", 29 | "landmark": "", 30 | "notes": "", 31 | "method": "DELIVERY", 32 | "status": "ENABLE" 33 | }, 34 | "basketItems": [ 35 | { 36 | "id": "3342a4d6-376b-4401-9b61-2a06fe8ab9ca", 37 | "internalId": 10055, 38 | "quantity": 1, 39 | "goodsId": "94869137-1918-462c-bbaf-895edb88d411", 40 | "goodsName": "Son bóng Sensual Spicy Nude Gloss - 105 Curious Boy", 41 | "goodsBrandName": "Hera", 42 | "hasInvoice": false, 43 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 44 | "imagePath": "529690f7-6f3b-4bc1-bfd6-ab9c8935953a.jpg", 45 | "defaultWholesalePrice": 372750, 46 | "retailPrice": 395000, 47 | "revisedRetailPrice": null, 48 | "marginPrice": 22250, 49 | "bulkPrice": 0, 50 | "supplierPrice": 355000, 51 | "soldPriceType": "RETAIL", 52 | "soldPrice": 395000, 53 | "totalSoldPrice": 395000, 54 | "isRevisedRetailPrice": true, 55 | "status": "ENABLE" 56 | } 57 | ], 58 | "commissionPay": { 59 | "id": "eb199b81-53d9-4f10-9425-11ea276000d5", 60 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 61 | "amount": 22250, 62 | "paymentAccountId": "9acd9277-28bd-4fc5-ba9b-47cac0ee88f5", 63 | "paymentMode": "BANK_TRANSFER", 64 | "status": "ENABLE", 65 | "bankName": null, 66 | "bankUserName": null, 67 | "bankAccountNo": null 68 | }, 69 | "invoice": { 70 | "id": "18bb9850-d195-4e2f-ad64-ccbb7ae459a3", 71 | "customerId": "380f1a81-9470-4354-8978-3cc5537b3a22", 72 | "invoiceType": "COMPANY", 73 | "email": "corporate@gmail.com", 74 | "companyName": "home sell", 75 | "taxCode": "TXN8989", 76 | "address": "1, south hall", 77 | "status": "ENABLE" 78 | }, 79 | "orderDate": "2022-09-11T07:40:47.242815Z", 80 | "supplierBankAccount": null, 81 | "supplierWalletAccount": null, 82 | "basketId": "28880452-6aa2-40f1-895f-4470bcd656db", 83 | "shippingFree": false 84 | } -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/MonthlyBadgeService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.enums.StatusType; 4 | import com.sellde.reward.repository.MonthlyBadgeRepository; 5 | import com.sellde.reward.service.mapper.MonthlyBadgeMapper; 6 | import com.sellde.reward.service.model.MonthlyBadgeModel; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import reactor.core.publisher.Flux; 10 | import reactor.core.publisher.Mono; 11 | 12 | import java.time.Instant; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | @Service 17 | public class MonthlyBadgeService { 18 | 19 | 20 | @Autowired 21 | private MonthlyBadgeRepository monthlyBadgeRepository; 22 | 23 | 24 | @Autowired 25 | private MonthlyBadgeMapper monthlyBadgeMapper; 26 | 27 | public Flux getAll() { 28 | return monthlyBadgeRepository.findAll() 29 | .map(monthlyBadgeMapper::toModel); 30 | } 31 | 32 | public Flux getAllEnabled() { 33 | return monthlyBadgeRepository.findAllByStatus(StatusType.ENABLE) 34 | .map(monthlyBadgeMapper::toModel); 35 | } 36 | 37 | public Mono> getAllEnabledIntoMap() { 38 | return monthlyBadgeRepository.findAllByStatus(StatusType.ENABLE) 39 | .map(monthlyBadgeMapper::toModel) 40 | .collectList() 41 | .map(list -> { 42 | var map = new HashMap(); 43 | list.forEach(item -> { 44 | map.put(item.getBadge(), item); 45 | }); 46 | return map; 47 | }); 48 | } 49 | 50 | // public Mono getBadgeByTotalPrice(BigDecimal price) { 51 | // return monthlyBadgeRepository.findAllByBadgeValueLessThanEqualAndStatus(price) 52 | // .map(monthlyBadgeMapper::toModel); 53 | // } 54 | // 55 | // public Mono getByBadge(String badgeName) { 56 | // return monthlyBadgeRepository.findOneByBadgeAndStatus(badgeName, StatusType.ENABLE) 57 | // .map(monthlyBadgeMapper::toModel); 58 | // } 59 | 60 | public Mono create(MonthlyBadgeModel model) { 61 | model.setId(null); 62 | model.setBadge(model.getBadge().toUpperCase()); 63 | return monthlyBadgeRepository.save(monthlyBadgeMapper.toEntity(model)) 64 | .map(monthlyBadgeMapper::toModel); 65 | } 66 | 67 | public Mono update(MonthlyBadgeModel model) { 68 | return monthlyBadgeRepository.findById(model.getMonthlyBadgeId()) 69 | .map(item -> { 70 | item.setStatus(model.getStatus()); 71 | item.setLastModifiedDate(Instant.now()); 72 | return item; 73 | }).flatMap(monthlyBadgeRepository::save) 74 | .map(monthlyBadgeMapper::toModel); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/SignupRewardService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.domain.SignupReward; 4 | import com.sellde.reward.enums.StatusType; 5 | import com.sellde.reward.repository.SignupRewardRepository; 6 | import com.sellde.reward.service.mapper.SignupRewardMapper; 7 | import com.sellde.reward.service.model.SignupRewardModel; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import reactor.core.publisher.Flux; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @Service 16 | public class SignupRewardService { 17 | 18 | @Autowired 19 | private SignupRewardRepository signupRewardRepository; 20 | 21 | @Autowired 22 | private SignupRewardMapper signupRewardMapper; 23 | 24 | public Flux getAllByEnableStatus() { 25 | return signupRewardRepository.findAllByStatusOrderByRewardType(StatusType.ENABLE) 26 | .bufferUntilChanged(SignupReward::getRewardType) 27 | .map(item -> { 28 | var model = signupRewardMapper.toModel(item.get(0)); 29 | var rangeList = signupRewardMapper.toRangeModelList(item); 30 | model.setRangeList(rangeList); 31 | return model; 32 | }) 33 | ; 34 | } 35 | 36 | public Flux getAll() { 37 | return signupRewardRepository.findAllOrderByRewardType() 38 | .bufferUntilChanged(SignupReward::getRewardType) 39 | .map(item -> { 40 | var model = signupRewardMapper.toModel(item.get(0)); 41 | var rangeList = signupRewardMapper.toRangeModelList(item); 42 | model.setRangeList(rangeList); 43 | return model; 44 | }) 45 | ; 46 | } 47 | 48 | public Flux createOrUpdate(List modelList) { 49 | var entityList = new ArrayList(); 50 | modelList.forEach(model -> { 51 | var rangeModelList = model.getRangeList(); 52 | var entityFromRangeList = signupRewardMapper.toRangeEntityList(rangeModelList); 53 | entityFromRangeList.forEach(item -> { 54 | item.setConfigName(model.getConfigName()); 55 | item.setConfigValue(model.getConfigValue()); 56 | item.setRewardType(model.getRewardType()); 57 | }); 58 | entityList.addAll(entityFromRangeList); 59 | }); 60 | 61 | return signupRewardRepository.saveAll(entityList) 62 | .bufferUntilChanged(SignupReward::getRewardType) 63 | .map(item -> { 64 | var model = signupRewardMapper.toModel(item.get(0)); 65 | var rangeList = signupRewardMapper.toRangeModelList(item); 66 | model.setRangeList(rangeList); 67 | return model; 68 | }) 69 | ; 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/SupplierSalesModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.sellde.reward.enums.SelectOption; 5 | import com.sellde.reward.enums.StatusType; 6 | import lombok.*; 7 | import lombok.experimental.FieldDefaults; 8 | 9 | import java.math.BigDecimal; 10 | import java.time.LocalDate; 11 | import java.util.UUID; 12 | 13 | public class SupplierSalesModel { 14 | 15 | @Data 16 | @AllArgsConstructor 17 | @NoArgsConstructor 18 | @FieldDefaults(level = AccessLevel.PRIVATE) 19 | public static class SqsMessage { 20 | String messageType; 21 | BasketRequest request; 22 | } 23 | 24 | @Data 25 | @With 26 | @AllArgsConstructor 27 | @NoArgsConstructor 28 | @FieldDefaults(level = AccessLevel.PRIVATE) 29 | public static class SqsMessageResponse { 30 | String status; 31 | String message; 32 | } 33 | 34 | @Data 35 | @With 36 | @AllArgsConstructor 37 | @FieldDefaults(level = AccessLevel.PRIVATE) 38 | @JsonIgnoreProperties(ignoreUnknown = true) 39 | public static class BasketRequest { 40 | UUID customerId; 41 | UUID basketId; 42 | int orderNo; 43 | int salesMonth; 44 | BigDecimal totalPrice; 45 | BigDecimal totalRelyBulkPrice; 46 | BigDecimal discountPrice; 47 | BigDecimal shippingPrice; 48 | LocalDate orderDate; 49 | StatusType status; 50 | SelectOption selectOption; 51 | String basketStatus; 52 | String customerName; 53 | String preCustomerBadge; 54 | String postCustomerBadge; 55 | long phoneNo; 56 | 57 | public BasketRequest() { 58 | status = StatusType.ENABLE; 59 | totalPrice = BigDecimal.ZERO; 60 | discountPrice = BigDecimal.ZERO; 61 | shippingPrice = BigDecimal.ZERO; 62 | } 63 | } 64 | 65 | @Data 66 | @NoArgsConstructor 67 | @FieldDefaults(level = AccessLevel.PRIVATE) 68 | public static class BasketResponse extends BasketRequest { 69 | UUID id; 70 | 71 | public UUID getSupplierSalesId() { 72 | return getId(); 73 | } 74 | } 75 | 76 | 77 | @Data 78 | @With 79 | @AllArgsConstructor 80 | @NoArgsConstructor 81 | @FieldDefaults(level = AccessLevel.PRIVATE) 82 | public static class CustomerBadgeRequest { 83 | UUID customerId; 84 | int basketOrderNo; 85 | String postCustomerBadge; 86 | Integer yearMonth; 87 | BigDecimal totalPrice; 88 | BigDecimal totalRelyBulkPrice; 89 | BigDecimal totalDiscountPrice; 90 | BigDecimal totalShippingPrice; 91 | } 92 | 93 | 94 | @Data 95 | @With 96 | @AllArgsConstructor 97 | @NoArgsConstructor 98 | @FieldDefaults(level = AccessLevel.PRIVATE) 99 | public static class SumOfBasketSales { 100 | UUID customerId; 101 | Integer yearMonth; 102 | BigDecimal totalPrice; 103 | BigDecimal totalDiscountPrice; 104 | BigDecimal totalShippingPrice; 105 | BigDecimal totalRelyBulkPrice; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/resources/db/migration/m20221015_monthly_badge_config.sql: -------------------------------------------------------------------------------- 1 | drop table if exists monthly_badge_brand_config; 2 | drop table if exists customer_badge; 3 | drop table if exists monthly_badge; 4 | 5 | 6 | CREATE TABLE monthly_badge 7 | ( 8 | id uuid DEFAULT uuid_generate_v4() primary key, 9 | badge varchar(50) default 'NA' unique, 10 | badge_index int NOT NULL default 0 unique, 11 | badge_value numeric(12, 2) default 0 unique, 12 | status varchar(15), 13 | created_date timestamptz, 14 | last_modified_date timestamptz 15 | ); 16 | 17 | CREATE TABLE monthly_badge_brand_config 18 | ( 19 | id uuid DEFAULT uuid_generate_v4() primary key, 20 | monthly_badge_id uuid NOT NULL, 21 | goods_brand_id uuid NOT NULL, 22 | goods_brand_name varchar(150), 23 | badge_discount_rate numeric(4, 2) default 0, 24 | badge_discount_price numeric(12, 2) default 0, 25 | status varchar(15), 26 | created_date timestamptz, 27 | last_modified_date timestamptz, 28 | 29 | CONSTRAINT fk_monthly_badge_brand_config_monthly_badge_id 30 | FOREIGN KEY (monthly_badge_id) REFERENCES monthly_badge (id) 31 | ); 32 | 33 | CREATE UNIQUE INDEX idx_monthly_badge_config_monthly_badge_id_brand_id ON monthly_badge_brand_config (monthly_badge_id, goods_brand_id); 34 | 35 | 36 | CREATE TABLE customer_badge 37 | ( 38 | id uuid DEFAULT uuid_generate_v4() primary key, 39 | customer_id uuid NOT NULL, 40 | year_month int NOT NULL, 41 | total_price numeric(12, 2) default 0, 42 | total_rely_bulk_price numeric(12, 2) default 0, 43 | default_badge_id uuid, 44 | default_badge varchar(25), 45 | monthly_badge_id uuid, 46 | monthly_badge varchar(25), 47 | note varchar(50) default 'NA', 48 | status varchar(15), 49 | created_date timestamptz, 50 | last_modified_date timestamptz, 51 | 52 | CONSTRAINT fk_customer_badge_monthly_badge_id 53 | FOREIGN KEY (monthly_badge_id) REFERENCES monthly_badge (id), 54 | CONSTRAINT fk_customer_badge_default_badge_id 55 | FOREIGN KEY (default_badge_id) REFERENCES monthly_badge (id) 56 | ); 57 | CREATE UNIQUE INDEX idx_customer_badge_customer_id_year_month ON customer_badge (customer_id, year_month); 58 | 59 | 60 | -- default data for monthly badge 61 | INSERT INTO monthly_badge (id, badge, badge_index, badge_value, status, created_date, last_modified_date) 62 | VALUES ('744f6da6-0f12-4bac-9111-f6deee2a3ebb', 'MEMBER', 1, 2000000.00, 'ENABLE', now(), now()), 63 | ('4e30ecd6-d0f9-4115-bf1e-5d690d19fc83', 'BRONZE', 2, 5000000.00, 'ENABLE', now(), now()), 64 | ('31101c43-9e62-496b-b9a4-e1438ab92492', 'SILVER', 3, 10000000.00, 'ENABLE', now(), now()), 65 | ('57330a4f-6fad-495c-8304-3077cc3184e2', 'GOLD', 4, 20000000.00, 'ENABLE', now(), now()), 66 | ('fb3424f5-1b3e-4b76-b0cf-66e7d4fe6f28', 'PLATINUM', 5, 50000000.00, 'ENABLE', now(), now()), 67 | ('f6de2820-263c-4fea-87dd-cdc524df7a53', 'DIAMOND', 6, 100000000.00, 'ENABLE', now(), now()), 68 | ('243335a8-651f-40d8-b3f6-631e6b1c3f32', 'NA', 0, 0.00, 'ENABLE', now(), now()); 69 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/biz/StorePromoBizService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.biz; 2 | 3 | import com.sellde.reward.service.DiscountCouponService; 4 | import com.sellde.reward.service.SettingService; 5 | import com.sellde.reward.service.ShippingPromoService; 6 | import com.sellde.reward.service.SignupRewardService; 7 | import com.sellde.reward.service.model.SettingModel; 8 | import com.sellde.reward.service.model.StorePromoModel; 9 | import com.sellde.reward.util.Constant; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Transactional; 13 | import reactor.core.publisher.Mono; 14 | 15 | import java.util.List; 16 | 17 | @Service 18 | @Transactional 19 | public class StorePromoBizService { 20 | 21 | @Autowired 22 | private SettingService settingService; 23 | 24 | @Autowired 25 | private ShippingPromoService shippingPromoService; 26 | 27 | @Autowired 28 | private SignupRewardService signupRewardService; 29 | 30 | @Autowired 31 | private DiscountCouponService discountCouponService; 32 | 33 | @Transactional(readOnly = true) 34 | public Mono findPromos() { 35 | return shippingPromoService.getAllActivePromos().collectList() 36 | .zipWith(settingService.getStorePromoList().collectList()) 37 | .map(tuple -> { 38 | var resultModel = new StorePromoModel(); 39 | resultModel.setShippingFeePromoList(tuple.getT1()); 40 | extractFromSettingList(resultModel, tuple.getT2()); 41 | return resultModel; 42 | }) 43 | .zipWith(signupRewardService.getAllByEnableStatus().collectList()) 44 | .map(tuple -> { 45 | var resultModel = tuple.getT1(); 46 | resultModel.setSignupRewardList(tuple.getT2()); 47 | return resultModel; 48 | }) 49 | .zipWith(discountCouponService.getAllByEnableStatus().collectList()) 50 | .map(tuple -> { 51 | var resultModel = tuple.getT1(); 52 | resultModel.setDiscountCouponList(tuple.getT2()); 53 | return resultModel; 54 | }); 55 | } 56 | 57 | private void extractFromSettingList(StorePromoModel storePromoModel, List settingModelList) { 58 | settingModelList.forEach(item -> { 59 | if (Constant.SHIPPING_PROMO_CONFIG.equalsIgnoreCase(item.getConfigName())) { 60 | storePromoModel.setStoreShippingFeePromoOn(item.getConfigValue().equalsIgnoreCase("true")); 61 | storePromoModel.setStoreShippingFeePromoType(item.getConfigType()); 62 | } 63 | if (Constant.SIGNUP_PROMO_CONFIG.equalsIgnoreCase(item.getConfigName())) { 64 | storePromoModel.setStoreSignupDiscountPromoOn(item.getConfigValue().equalsIgnoreCase("true")); 65 | storePromoModel.setStoreSignupBonusPromoType(item.getConfigType()); 66 | } 67 | if (Constant.DISCOUNT_COUPON_CONFIG.equalsIgnoreCase(item.getConfigName())) { 68 | storePromoModel.setStoreDiscountCouponPromoOn(item.getConfigValue().equalsIgnoreCase("true")); 69 | storePromoModel.setStoreDiscountCouponPromoType(item.getConfigType()); 70 | } 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/biz/CustomerBadgeReportService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.biz; 2 | 3 | import com.sellde.reward.service.CustomerBadgeService; 4 | import com.sellde.reward.service.UserProfileCopyService; 5 | import com.sellde.reward.service.model.CustomerBadgeModel; 6 | import com.sellde.reward.service.model.CustomerBadgeReport; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | import org.springframework.transaction.annotation.Transactional; 10 | import reactor.core.publisher.Flux; 11 | import reactor.core.publisher.Mono; 12 | 13 | import java.time.LocalDate; 14 | import java.util.Arrays; 15 | 16 | @Service 17 | @Transactional(readOnly = true) 18 | public class CustomerBadgeReportService { 19 | 20 | @Autowired 21 | UserProfileCopyService userProfileCopyService; 22 | 23 | @Autowired 24 | private CustomerBadgeService customerBadgeService; 25 | 26 | public Flux createReport() { 27 | 28 | var today = LocalDate.now(); 29 | var currentYearMonth = (today.getYear() * 100) + today.getMonthValue(); 30 | var currentMonthName = today.getMonth().name(); 31 | 32 | var lastMonthDate = today.minusMonths(1); 33 | var lastYearMonth = (lastMonthDate.getYear() * 100) + lastMonthDate.getMonthValue(); 34 | var lastMonthName = lastMonthDate.getMonth().name(); 35 | 36 | var nextMonthDate = today.plusMonths(1); 37 | var nextYearMonth = (nextMonthDate.getYear() * 100) + nextMonthDate.getMonthValue(); 38 | var nextMonthName = nextMonthDate.getMonth().name(); 39 | 40 | var yearMonthList = Arrays.asList(lastYearMonth, currentYearMonth, nextYearMonth); 41 | /* find all unique customerId for last-current-next month */ 42 | var value = customerBadgeService.getAllCustomerIdByYearMonth(yearMonthList) 43 | .bufferUntilChanged(CustomerBadgeModel::getCustomerId) 44 | .map(itemList -> { 45 | final var report = new CustomerBadgeReport() 46 | .withCurrentMonth(currentYearMonth) 47 | .withCurrentMonthName(currentMonthName) 48 | .withLastMonth(lastYearMonth) 49 | .withLastMonthName(lastMonthName) 50 | .withNextMonth(nextYearMonth) 51 | .withNextMonthName(nextMonthName); 52 | 53 | itemList.forEach(item -> { 54 | var customerId = item.getCustomerId(); 55 | report.setCustomerId(customerId); 56 | if (item.getYearMonth() == lastYearMonth) { 57 | report.setLastMonthBadge(item); 58 | } else if (item.getYearMonth() == currentYearMonth) { 59 | report.setCurrentMonthBadge(item); 60 | } else { 61 | report.setNextMonthBadge(item); 62 | } 63 | }); 64 | return report; 65 | }) 66 | .flatMap(this::updateCustomer); 67 | return value; 68 | } 69 | 70 | private Mono updateCustomer(CustomerBadgeReport report) { 71 | return userProfileCopyService.getByUserId(report.getCustomerId()) 72 | .map(user -> { 73 | report.setCustomerName(user.getUserName()); 74 | report.setPhoneNo(user.getPhoneNo()); 75 | return report; 76 | }); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/SettingService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.repository.SettingRepository; 4 | import com.sellde.reward.service.mapper.SettingMapper; 5 | import com.sellde.reward.service.model.SettingModel; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | import reactor.core.publisher.Flux; 9 | import reactor.core.publisher.Mono; 10 | 11 | import java.util.Arrays; 12 | 13 | import static com.sellde.reward.util.Constant.*; 14 | 15 | @Service 16 | public class SettingService { 17 | 18 | 19 | @Autowired 20 | private SettingRepository settingRepository; 21 | @Autowired 22 | private SettingMapper settingMapper; 23 | 24 | public Mono isShippingFeePromoOn() { 25 | return settingRepository 26 | .findDistinctByConfigName(SHIPPING_PROMO_CONFIG) 27 | .map(domain -> domain.getConfigValue().equalsIgnoreCase("true")); 28 | } 29 | 30 | public Mono getShippingFeePromoOn() { 31 | return settingRepository 32 | .findDistinctByConfigName(SHIPPING_PROMO_CONFIG) 33 | .map(settingMapper::toModel) 34 | ; 35 | } 36 | 37 | public Flux getStorePromoList() { 38 | var promoNameList = Arrays.asList(SHIPPING_PROMO_CONFIG, 39 | SIGNUP_PROMO_CONFIG, 40 | DISCOUNT_COUPON_CONFIG 41 | ); 42 | return settingRepository 43 | .findAllByConfigNameIsIn(promoNameList) 44 | .map(settingMapper::toModel) 45 | ; 46 | } 47 | 48 | public Mono updateShippingFeePromo(Boolean isShippingFeePromoOn) { 49 | return settingRepository.findDistinctByConfigName(SHIPPING_PROMO_CONFIG) 50 | .doOnNext(domain -> { 51 | domain.setConfigValue(isShippingFeePromoOn.toString()); 52 | }).flatMap(settingRepository::save) 53 | .map(domain -> { 54 | return domain.getConfigValue().equalsIgnoreCase("true"); 55 | }); 56 | 57 | } 58 | 59 | public Mono updateStoreSignupDiscountPromoOn(Boolean isStoreSignupDiscountPromoOn) { 60 | return settingRepository.findDistinctByConfigName(SIGNUP_PROMO_CONFIG) 61 | .doOnNext(domain -> { 62 | domain.setConfigValue(isStoreSignupDiscountPromoOn.toString()); 63 | }).flatMap(settingRepository::save) 64 | .map(domain -> domain.getConfigValue().equalsIgnoreCase("true")); 65 | 66 | } 67 | 68 | public Mono updateStoreDiscountCouponPromoOn(Boolean isStoreDiscountCouponPromoOn) { 69 | return settingRepository.findDistinctByConfigName(DISCOUNT_COUPON_CONFIG) 70 | .doOnNext(domain -> { 71 | domain.setConfigValue(isStoreDiscountCouponPromoOn.toString()); 72 | }).flatMap(settingRepository::save) 73 | .map(domain -> domain.getConfigValue().equalsIgnoreCase("true")); 74 | 75 | } 76 | 77 | public Mono isStoreSignupDiscountPromoOn() { 78 | return settingRepository 79 | .findDistinctByConfigName(SIGNUP_PROMO_CONFIG) 80 | .map(domain -> domain.getConfigValue().equalsIgnoreCase("true")); 81 | } 82 | 83 | public Mono isStoreDiscountCouponPromoOn() { 84 | return settingRepository 85 | .findDistinctByConfigName(DISCOUNT_COUPON_CONFIG) 86 | .map(domain -> domain.getConfigValue().equalsIgnoreCase("true")); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/model/CustomerMonthlyBadgeModel.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service.model; 2 | 3 | 4 | import com.sellde.reward.enums.CustomerMonthlyBadge; 5 | import lombok.AccessLevel; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Data; 8 | import lombok.With; 9 | import lombok.experimental.FieldDefaults; 10 | 11 | import java.time.LocalDate; 12 | import java.util.ArrayList; 13 | import java.util.HashMap; 14 | import java.util.List; 15 | 16 | @Data 17 | @With 18 | @AllArgsConstructor 19 | @FieldDefaults(level = AccessLevel.PRIVATE) 20 | public class CustomerMonthlyBadgeModel { 21 | 22 | // private CustomerMonthlyBadge currentMonthBadge; 23 | private List monthlySalesList; 24 | 25 | public CustomerMonthlyBadgeModel() { 26 | this.monthlySalesList = new ArrayList<>(); 27 | 28 | } 29 | 30 | 31 | /** 32 | * NA(last-month) && BRONZE/SILVER(current-month) => 33 | *

34 | * BRONZE(last-month) && NA/SILVER(current-month) => 35 | *

36 | * SILVER(last-month) && NA/BRONZE(current-month) => 37 | */ 38 | public void getCurrentMonthBadge() { 39 | // var thisMonth = LocalDate.now().getMonth().getValue(); 40 | // var lastMonth = LocalDate.now().minusMonths(1).getMonth().getValue(); 41 | // var last2ndMonth = LocalDate.now().minusMonths(2).getMonth().getValue(); 42 | // 43 | // var mapSales = findCurrentMonthlySales(); 44 | // var thisMonthSales = mapSales.get(thisMonth); 45 | // var lastMonthSales = mapSales.get(lastMonth); 46 | // var last2ndMonthSales = mapSales.get(last2ndMonth); 47 | // 48 | // var thisMonthBadge = thisMonthSales.getCustomerBadge(); 49 | // var lastMonthBadge = lastMonthSales.getCustomerBadge(); 50 | // 51 | // 52 | // var currentMonthBadge = CustomerMonthlyBadge.NA; 53 | 54 | // if (previousMonthBadge == CustomerMonthlyBadge.NA) { 55 | // /* when previous month has no badge */ 56 | // currentMonthBadge = thisMonthBadge; 57 | // } 58 | // 59 | // 60 | // if (previousMonthBadge == CustomerMonthlyBadge.BRONZE) { 61 | // if (thisMonthBadge == CustomerMonthlyBadge.NA) { 62 | // currentMonthBadge = previousMonthBadge; 63 | // } else { 64 | // currentMonthBadge = thisMonthBadge; 65 | // } 66 | // } 67 | // 68 | // if (previousMonthBadge == CustomerMonthlyBadge.SILVER) { 69 | // if (thisMonthBadge == CustomerMonthlyBadge.NA) { 70 | // currentMonthBadge = previousMonthBadge; 71 | // } else if (thisMonthBadge == CustomerMonthlyBadge.BRONZE) { 72 | // currentMonthBadge = previousMonthBadge; 73 | // } else { 74 | // currentMonthBadge = thisMonthBadge; 75 | // } 76 | // } 77 | 78 | 79 | } 80 | 81 | // 82 | // private HashMap findCurrentMonthlySales() { 83 | // var currentMonth = LocalDate.now().getMonth().getValue(); 84 | // var map = new HashMap(); 85 | // for (var model : monthlySalesList) { 86 | // map.put(model.getSalesMonth(), model); 87 | // } 88 | // 89 | // if (!map.containsKey(currentMonth)) { 90 | // var customerId = monthlySalesList.get(0).getCustomerId(); 91 | // var currentYear = LocalDate.now().getYear(); 92 | // map.put(currentMonth, new SupplierSalesModel() 93 | // .withSalesMonth(currentMonth) 94 | // .withSalesYear(currentYear) 95 | // .withCustomerId(customerId)); 96 | // } 97 | // return map; 98 | // } 99 | } 100 | -------------------------------------------------------------------------------- /.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/sellde/reward/service/SignupQnAService.java: -------------------------------------------------------------------------------- 1 | package com.sellde.reward.service; 2 | 3 | import com.sellde.reward.domain.SignupQnA; 4 | import com.sellde.reward.repository.SignupQnARepository; 5 | import com.sellde.reward.service.mapper.SignupQnAMapper; 6 | import com.sellde.reward.service.model.SignupQnAModel; 7 | import com.sellde.reward.util.Constant; 8 | import org.json.JSONObject; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import reactor.core.publisher.Flux; 12 | import reactor.core.publisher.Mono; 13 | import reactor.util.function.Tuples; 14 | 15 | import java.time.Duration; 16 | import java.time.Instant; 17 | import java.util.*; 18 | import java.util.stream.Collectors; 19 | 20 | @Service 21 | public class SignupQnAService { 22 | 23 | @Autowired 24 | private SignupQnARepository signupQnARepository; 25 | 26 | @Autowired 27 | private SignupQnAMapper signupQnAMapper; 28 | 29 | public Flux getAllDefaultQuery() { 30 | return signupQnARepository.findAllByPhoneNoOrderByQueryNo(Constant.SYSTEM_PHONE_NO) 31 | .map(signupQnAMapper::toOnlyQuery); 32 | } 33 | 34 | public Mono isAnsweredByPhoneNo(long phoneNo) { 35 | return signupQnARepository.findAllByPhoneNoOrderByQueryNo(Constant.SYSTEM_PHONE_NO) 36 | .collectList() 37 | .zipWith(signupQnARepository.findAllByPhoneNoAndAnswerIsNotNullOrderByQueryNo(phoneNo).collectList()) 38 | .map(tuple -> { 39 | var selldeQna = tuple.getT1(); 40 | var custQna = tuple.getT2(); 41 | if (custQna.isEmpty()) { 42 | return false; 43 | } 44 | return compareAnswer(selldeQna, custQna); 45 | }); 46 | } 47 | 48 | public Mono isValidAnswerByCustomer(List requestList) { 49 | var customerPhoneNo = requestList.get(0).getPhoneNo(); 50 | 51 | /* first find all if existing anc compare answer & validate time for 24hr */ 52 | 53 | var selldeAns = signupQnARepository 54 | .findAllByPhoneNoOrderByQueryNo(Constant.SYSTEM_PHONE_NO) 55 | .collectList(); 56 | var existingAnsMono = signupQnARepository 57 | .findAllByPhoneNoOrderByQueryNo(customerPhoneNo) 58 | .collectList(); 59 | 60 | var result = new JSONObject(); 61 | var value = Mono.zip(selldeAns, existingAnsMono) 62 | .map(tuple -> { 63 | var selldeAnsList = tuple.getT1(); 64 | var existingAnsList = tuple.getT2(); 65 | if (tuple.getT2().isEmpty()) { 66 | return Tuples.of(selldeAnsList, existingAnsList, true); 67 | } 68 | var isValidAnswer = compareAnswer(selldeAnsList, existingAnsList); 69 | var lastUpdatedTime = (existingAnsList.isEmpty()) ? Instant.now() 70 | : existingAnsList.get(0).getLastModifiedDate(); 71 | var isValidAnswerWithIn24hrs = true; 72 | if (Boolean.FALSE == isValidAnswer && 73 | /* hours difference needs to be 24 hrs */ 74 | (Duration.between(lastUpdatedTime, Instant.now()).toHours() < 24)) { 75 | isValidAnswerWithIn24hrs = false; 76 | } 77 | return Tuples.of(selldeAnsList, existingAnsList, isValidAnswerWithIn24hrs); 78 | }) 79 | .flatMap(tuple3 -> { 80 | var isValidAnswerWithIn24hrs = tuple3.getT3(); 81 | var lastUpdatedTime = (tuple3.getT2().isEmpty()) ? Instant.now() 82 | : tuple3.getT2().get(0).getLastModifiedDate(); 83 | if (Boolean.FALSE == isValidAnswerWithIn24hrs) { 84 | return Mono.just( 85 | result.put("isValidSignup", false) 86 | .put("code", "INVALID_DURATION") 87 | .put("message", "User try to login in 24hrs after failed to signup") 88 | ); 89 | } else { 90 | return validateAndUpdate(tuple3.getT1(), tuple3.getT2(), requestList); 91 | } 92 | }); 93 | return value; 94 | } 95 | 96 | 97 | private Mono validateAndUpdate( 98 | List selldeAnsList, List userExistingAnsList, 99 | List newRequestList) { 100 | 101 | final var mapExistingUserAns = new HashMap(); 102 | List domainList = null; 103 | if (userExistingAnsList.isEmpty()) { 104 | domainList = signupQnAMapper.toEntity(newRequestList); 105 | } else { 106 | userExistingAnsList.forEach(item -> { 107 | mapExistingUserAns.put(item.getQueryNo(), item); 108 | }); 109 | domainList = newRequestList.stream() 110 | .map(newItem -> { 111 | var qno = newItem.getQueryNo(); 112 | if (mapExistingUserAns.containsKey(qno)) { 113 | return signupQnAMapper.toUpdateEntity(mapExistingUserAns.get(qno), newItem); 114 | } else { 115 | return signupQnAMapper.toEntity(newItem); 116 | } 117 | }).collect(Collectors.toList()); 118 | } 119 | return signupQnARepository.saveAll(domainList) 120 | .collectList() 121 | .map(savedItemList -> compareAnswer(selldeAnsList, savedItemList)) 122 | .map(isValid -> new JSONObject().put("isValidSignup", isValid)); 123 | } 124 | 125 | 126 | private Boolean compareAnswer(List selldeAnsList, List userAnsList) { 127 | var result = new JSONObject(); 128 | if (userAnsList.size() != selldeAnsList.size()) { 129 | return false; 130 | } 131 | var mapUserAns = new HashMap(); 132 | userAnsList.forEach(item -> { 133 | mapUserAns.put(item.getQueryNo(), item.getAnswer()); 134 | }); 135 | for (var selldeItem : selldeAnsList) { 136 | var queryNo = selldeItem.getQueryNo(); 137 | var userAns = mapUserAns.get(queryNo).trim().toLowerCase(); 138 | var selldeAnsValues = Arrays.asList(selldeItem.getAnswer().trim().toLowerCase().split(",")); 139 | if (Boolean.FALSE == (selldeAnsValues.contains(userAns))) { 140 | result.put("isValidSignup", false) 141 | .put("code", "INVALID_SIGNUP_QNA") 142 | .put("message", "Invalid answers"); 143 | return false; 144 | } 145 | } 146 | return true; 147 | } 148 | 149 | } 150 | --------------------------------------------------------------------------------