diff --git a/src/main/java/project/trendpick_pro/domain/common/view/entity/View.java b/src/main/java/project/trendpick_pro/domain/common/view/entity/View.java new file mode 100644 index 00000000..dc3d8aa6 --- /dev/null +++ b/src/main/java/project/trendpick_pro/domain/common/view/entity/View.java @@ -0,0 +1,23 @@ +package project.trendpick_pro.domain.common.view.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@NoArgsConstructor +public class View { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long count = 0L; + + public void increment() { + this.count++; + } +} diff --git a/src/main/java/project/trendpick_pro/domain/common/view/repository/ViewRepository.java b/src/main/java/project/trendpick_pro/domain/common/view/repository/ViewRepository.java new file mode 100644 index 00000000..0d9df331 --- /dev/null +++ b/src/main/java/project/trendpick_pro/domain/common/view/repository/ViewRepository.java @@ -0,0 +1,7 @@ +package project.trendpick_pro.domain.common.view.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import project.trendpick_pro.domain.common.view.entity.View; + +public interface ViewRepository extends JpaRepository { +} diff --git a/src/main/java/project/trendpick_pro/domain/common/view/service/ViewService.java b/src/main/java/project/trendpick_pro/domain/common/view/service/ViewService.java new file mode 100644 index 00000000..f28b649e --- /dev/null +++ b/src/main/java/project/trendpick_pro/domain/common/view/service/ViewService.java @@ -0,0 +1,49 @@ +package project.trendpick_pro.domain.common.view.service; + +import jakarta.servlet.http.HttpSession; +import lombok.RequiredArgsConstructor; +import org.springframework.kafka.annotation.KafkaListener; +import org.springframework.kafka.core.KafkaTemplate; +import org.springframework.messaging.handler.annotation.Payload; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import project.trendpick_pro.domain.common.view.entity.View; +import project.trendpick_pro.domain.common.view.repository.ViewRepository; + +@Service +@RequiredArgsConstructor +public class ViewService { + + private final ViewRepository viewRepository; + + private final KafkaTemplate kafkaTemplate; + + @Transactional + public void registerView() { + viewRepository.save(new View()); + } + + @Transactional(readOnly = true) + public int findSize() { + return viewRepository.findAll().size(); + } + + public void requestIncrementViewCount(HttpSession session) { + if (session.getAttribute("visited") == null) { + kafkaTemplate.send("views", "increment", "1"); + session.setAttribute("visited", true); + } + } + + @Transactional + @KafkaListener(topicPattern = "views", groupId = "group_id") + public void handleIncrementViewCount(@Payload String viewId) { + View totalView = viewRepository.findById(Long.valueOf(viewId)).get(); + totalView.increment(); + } + + @Transactional(readOnly = true) + public Long getCount() { + return viewRepository.findById(1L).get().getCount(); + } +} diff --git a/src/main/java/project/trendpick_pro/domain/product/controller/ProductController.java b/src/main/java/project/trendpick_pro/domain/product/controller/ProductController.java index a6483307..74c5bd95 100644 --- a/src/main/java/project/trendpick_pro/domain/product/controller/ProductController.java +++ b/src/main/java/project/trendpick_pro/domain/product/controller/ProductController.java @@ -1,5 +1,6 @@ package project.trendpick_pro.domain.product.controller; +import jakarta.servlet.http.HttpSession; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -20,6 +21,7 @@ import project.trendpick_pro.domain.category.service.MainCategoryService; import project.trendpick_pro.domain.category.service.SubCategoryService; import project.trendpick_pro.domain.common.base.rq.Rq; +import project.trendpick_pro.domain.common.view.service.ViewService; import project.trendpick_pro.domain.member.entity.Member; import project.trendpick_pro.domain.member.service.MemberService; import project.trendpick_pro.domain.product.entity.dto.ProductRequest; @@ -62,6 +64,8 @@ public class ProductController { private final ReviewService reviewService; private final AskService askService; + private final ViewService viewService; + private final Rq rq; @Value("${colors}") @@ -133,7 +137,10 @@ public String showProduct(@PathVariable Long productId, Pageable pageable, Model public String showAllProduct(@RequestParam(value = "page", defaultValue = "0") int offset, @RequestParam(value = "main-category", defaultValue = "all") String mainCategory, @RequestParam(value = "sub-category", defaultValue = "전체") String subCategory, - Pageable pageable, Model model) { + Pageable pageable, Model model, HttpSession session) throws InterruptedException { + viewService.requestIncrementViewCount(session); + Thread.sleep(200); + model.addAttribute("totalView", viewService.getCount()); if (mainCategory.equals("recommend")) { mainCategory = "추천"; } else if (mainCategory.equals("all")) { diff --git a/src/main/java/project/trendpick_pro/domain/product/service/ProductService.java b/src/main/java/project/trendpick_pro/domain/product/service/ProductService.java index b5abdaff..c7260e44 100644 --- a/src/main/java/project/trendpick_pro/domain/product/service/ProductService.java +++ b/src/main/java/project/trendpick_pro/domain/product/service/ProductService.java @@ -155,6 +155,7 @@ public void delete(Long productId) { } @Cacheable(value = "product", key = "#productId") + @Transactional(readOnly = true) public ProductResponse getProduct(Long productId) { Product product = productRepository.findById(productId).orElseThrow(() -> new ProductNotFoundException("존재하지 않는 상품입니다.")); @@ -284,6 +285,7 @@ public List getRecommendProduct(Member member) { return products; } + @Transactional(readOnly = true) public RsData> findProductsBySeller(Member member, int offset) { if (member.getBrand() == null) RsData.of("F-1", "브랜드 관리자의 브랜드를 알 수 없습니다. 브랜드를 설정하세요."); diff --git a/src/main/java/project/trendpick_pro/global/basedata/BaseData.java b/src/main/java/project/trendpick_pro/global/basedata/BaseData.java index 5e9b1356..246f11e6 100644 --- a/src/main/java/project/trendpick_pro/global/basedata/BaseData.java +++ b/src/main/java/project/trendpick_pro/global/basedata/BaseData.java @@ -23,6 +23,7 @@ import project.trendpick_pro.domain.category.service.MainCategoryService; import project.trendpick_pro.domain.category.service.SubCategoryService; import project.trendpick_pro.domain.common.file.CommonFile; +import project.trendpick_pro.domain.common.view.service.ViewService; import project.trendpick_pro.domain.coupon.entity.Coupon; import project.trendpick_pro.domain.coupon.entity.dto.request.StoreCouponSaveRequest; import project.trendpick_pro.domain.coupon.repository.CouponRepository; @@ -96,6 +97,7 @@ CommandLineRunner initData( BrandService brandService, RecommendService recommendService, ProductService productService, + ViewService viewService, ProductRepository productRepository, ReviewRepository reviewRepository, CouponRepository couponRepository, @@ -117,10 +119,10 @@ public void run(String... args) { accessKeyMap.put("secretKey", secretKey); accessKeyMap.put("bucket", bucket); - int memberCount = 10; - int productCount = 100; - int reviewCount = 0; - int couponCount = 0; + int memberCount = 100; + int productCount = 1000; + int reviewCount = 100; + int couponCount = 100; String brandName = "polo"; saveMembers(memberCount, tagNameService, memberService, recommendService); @@ -132,6 +134,10 @@ public void run(String... args) { saveReviews(reviewCount, productCount, accessKeyMap, memberService, productService ,reviewRepository); saveStoreCoupon(couponCount, storeRepository, couponRepository, brandService); + if (viewService.findSize() == 0) { + viewService.registerView(); + } + log.info("BASE_DATA_SUCCESS"); } }; diff --git a/src/main/java/project/trendpick_pro/global/config/RedisConfig.java b/src/main/java/project/trendpick_pro/global/config/RedisConfig.java deleted file mode 100644 index d094cd5a..00000000 --- a/src/main/java/project/trendpick_pro/global/config/RedisConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package project.trendpick_pro.global.config; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.CacheManager; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; -import org.springframework.data.redis.cache.RedisCacheManager; -import org.springframework.data.redis.connection.RedisStandaloneConfiguration; -import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import org.springframework.data.redis.core.RedisTemplate; - -@Configuration -@EnableCaching -@Profile("prod") -public class RedisConfig { - - @Value("${spring.redis.host}") - private String redisHost; - - @Value("${spring.redis.port}") - private int redisPort; - - @Bean - public JedisConnectionFactory jedisConnectionFactory() { - RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration(redisHost, redisPort); - return new JedisConnectionFactory(redisStandaloneConfiguration); - } - - @Bean - public RedisTemplate redisTemplate() { - RedisTemplate redisTemplate = new RedisTemplate<>(); - redisTemplate.setConnectionFactory(jedisConnectionFactory()); - return redisTemplate; - } - - @Bean - public CacheManager cacheManager() { - RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager - .RedisCacheManagerBuilder - .fromConnectionFactory(jedisConnectionFactory()); - return builder.build(); - } -} diff --git a/src/main/resources/templates/trendpick/orders/order-form.html b/src/main/resources/templates/trendpick/orders/order-form.html index e97447a8..7641bb17 100644 --- a/src/main/resources/templates/trendpick/orders/order-form.html +++ b/src/main/resources/templates/trendpick/orders/order-form.html @@ -199,7 +199,6 @@

결제 정보

}) }) - var couponPopup = null; function showCouponList(orderItemId) { document.getElementById('main-content').style.pointerEvents = 'none'; toastr.options = { @@ -210,12 +209,11 @@

결제 정보

tapToDismiss: false, // 탭하여 닫기 비활성화 closeButton: true // 닫기 버튼 활성화 }; - // AJAX 요청을 통해 쿠폰 목록 데이터를 가져옴 $.ajax({ - url: '/trendpick/usr/couponCards/apply', // 쿠폰 목록을 제공하는 API 엔드포인트의 URL + url: '/trendpick/usr/couponCards/apply', method: 'GET', data: { - orderItem: orderItemId // orderItemId 변수 값을 넘겨줌 + orderItem: orderItemId }, success: function(response) { couponListHTML = '
\n' + @@ -224,7 +222,6 @@

결제 정보

'쿠폰목록\n' + '' + '
\n'; - // 쿠폰 목록 데이터를 HTML로 변환하여 팝업 창에 삽입 response.forEach(function(coupon) { couponListHTML += `
@@ -248,10 +245,8 @@

${coupon.discountPercent}% 쿠폰

couponListHTML += '
\n' + '
' - // 팝업 창을 생성하여 쿠폰 목록을 표시 var couponPopup = toastr.info(couponListHTML, '', { closeButton: false, - // positionClass: 'toast-top-full-width', timeOut: 0, extendedTimeOut: 0, tapToDismiss: false @@ -274,11 +269,9 @@

${coupon.discountPercent}% 쿠폰

couponCard: couponCardId, orderItem: orderItemId }, - success: function(response) { + success: function() { toastr.success('쿠폰이 적용되었습니다.'); location.reload(); - // 원하는 동작 수행 (예: 페이지 새로고침, 필요한 데이터 업데이트 등) - // ... }, error: function() { toastr.error('쿠폰 적용에 실패했습니다.'); diff --git a/src/main/resources/templates/trendpick/products/list.html b/src/main/resources/templates/trendpick/products/list.html index 0ca08994..36446d27 100644 --- a/src/main/resources/templates/trendpick/products/list.html +++ b/src/main/resources/templates/trendpick/products/list.html @@ -105,6 +105,11 @@

+
+

누적 접속자:

+
+