Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PR] develop merge #62

Merged
merged 15 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.gradle/
.idea/
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@SpringBootApplication
@EnableAspectJAutoProxy
@EnableScheduling
@EnableEncryptableProperties
public class OnionHotSayYoApplication {

Expand All @@ -29,8 +31,4 @@ public ModelMapper modelMapper() {

@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder(){ return new BCryptPasswordEncoder();}
}


/* 테스트용 주석 2 */
/* Jenkins 작동 확인을 위한 커밋용 주석 */
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package org.omoknoone.onionhotsayyo.hotpost.aggregate;

import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.hibernate.annotations.CreationTimestamp;

import java.time.LocalDateTime;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@ToString
@Entity
@Table(name = "hotpost")
public class HotPost {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "hotpost_id")
private Integer hotPostId;

@Column(name = "is_active")
private boolean isActive;

@JoinColumn(name = "category_id")
private Integer categoryId;

@JoinColumn(name = "post_id")
private Integer postId;

// 상태 변경 메소드
public void activate() {
this.isActive = true;
}

public void deactivate() {
this.isActive = false;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package org.omoknoone.onionhotsayyo.hotpost.controller;

import org.omoknoone.onionhotsayyo.hotpost.dto.HotPostListByCategoryDTO;
import org.omoknoone.onionhotsayyo.hotpost.service.HotPostService;
import org.omoknoone.onionhotsayyo.hotpost.vo.ResponseHotPostListByCategory;
import org.omoknoone.onionhotsayyo.post.service.PostServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;
import java.util.List;

@RestController
@RequestMapping("/posts")
public class HotPostController {

private static final Logger log = LoggerFactory.getLogger(PostServiceImpl.class);

private final HotPostService hotPostService;

@Autowired
public HotPostController(HotPostService hotPostService) {
this.hotPostService = hotPostService;
}

@GetMapping("/hotlist/{categoryId}")
public ResponseEntity<ResponseHotPostListByCategory> viewHotPostList(@PathVariable Integer categoryId) {
LocalDate yesterday = LocalDate.now().minusDays(1); // 어제 날짜로 설정

List<HotPostListByCategoryDTO> hotPosts = hotPostService.getHotPostsByCategory(categoryId, yesterday);
if (hotPosts.isEmpty()) {
log.warn("카테고리 ID {}에 해당하는 상위 게시물이 없습니다.", categoryId);
return ResponseEntity.noContent().build();
}

ResponseHotPostListByCategory response = new ResponseHotPostListByCategory(hotPosts);
log.info("카테고리 ID {}에 대한 추천 게시물 목록 조회 완료", categoryId);

return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.omoknoone.onionhotsayyo.hotpost.dto;

import lombok.*;

import java.time.LocalDateTime;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@ToString
public class HotPostListByCategoryDTO {

private Integer categoryId;
private Integer postId;
private String title;
private LocalDateTime postedDate;
private int hits;
private boolean isDeleted;
private boolean isActive;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.omoknoone.onionhotsayyo.hotpost.repository;

import org.omoknoone.onionhotsayyo.hotpost.aggregate.HotPost;
import org.omoknoone.onionhotsayyo.hotpost.dto.HotPostListByCategoryDTO;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Repository
public interface HotPostRepository extends JpaRepository<HotPost, Integer> {

@Modifying
@Transactional
@Query("UPDATE HotPost h SET h.isActive = false WHERE h.categoryId = :categoryId")
void deactivateHotPostsByCategory(Integer categoryId);

// List<HotPost> findAllByCategoryIdAndIsActive(Integer categoryId, boolean isActive);

@Query("SELECT new org.omoknoone.onionhotsayyo.hotpost.dto.HotPostListByCategoryDTO(" +
"hp.categoryId, hp.postId, p.title, p.postedDate, p.hits, p.isDeleted, hp.isActive) " +
"FROM HotPost hp JOIN Post p ON hp.postId = p.postId " +
"WHERE hp.categoryId = :categoryId AND hp.isActive = true")
List<HotPostListByCategoryDTO> findHotPostsDetailsByCategory(@Param("categoryId") Integer categoryId);

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.omoknoone.onionhotsayyo.hotpost.service;

import org.omoknoone.onionhotsayyo.hotpost.dto.HotPostListByCategoryDTO;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;

public interface HotPostService {

void dailyUpdateHotPosts();

// 카테고리별 조회수 상위 5건의 추천글 목록 조회 (자정마다 업데이트)
List<HotPostListByCategoryDTO> updateHotPosts(Integer categoryId, LocalDate day);

// 조회된 카테고리별 5건의 추천글을 컨트롤러에서 호출
List<HotPostListByCategoryDTO> getHotPostsByCategory(Integer categoryId, LocalDate yesterday);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.omoknoone.onionhotsayyo.hotpost.service;

import org.modelmapper.ModelMapper;
import org.omoknoone.onionhotsayyo.hotpost.aggregate.HotPost;
import org.omoknoone.onionhotsayyo.hotpost.dto.HotPostListByCategoryDTO;
import org.omoknoone.onionhotsayyo.hotpost.repository.HotPostRepository;
import org.omoknoone.onionhotsayyo.post.dto.PostListByCategoryDTO;
import org.omoknoone.onionhotsayyo.post.service.PostService;
import org.omoknoone.onionhotsayyo.post.service.PostServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class HotPostServiceImpl implements HotPostService {

private static final Logger log = LoggerFactory.getLogger(PostServiceImpl.class);
private final ModelMapper modelMapper;
private final HotPostRepository hotPostRepository;
private final PostService postService;

@Autowired
public HotPostServiceImpl(ModelMapper modelMapper, HotPostRepository hotPostRepository, PostService postService) {
this.modelMapper = modelMapper;
this.hotPostRepository = hotPostRepository;
this.postService = postService;
}

/* 필기. 카테고리별 조회 수 상위 5건의 메소드 구현 */
// @Scheduled(cron = "0 0 0 * * ?") // 매일 자정 실행
@Scheduled(cron = "0 */10 * * * *") // 매 10분마다 실행
@Transactional
@Override
public void dailyUpdateHotPosts() {
// LocalDate yesterday = LocalDate.now().minusDays(1); // 어제 날짜 계산
LocalDate yesterday = LocalDate.now(); // 현재 날짜 계산
log.info("일일 추천 게시물 업데이트 시작");

Integer[] categoryIds = {1, 2, 3, 4, 5, 6, 7, 8, 9}; //카테고리 ID가 1~9까지 있음
for (Integer categoryId : categoryIds) {
updateHotPosts(categoryId, yesterday);
}
log.info("모든 카테고리에 대한 게시물 업데이트 완료");
}

@Transactional
@Override
public List<HotPostListByCategoryDTO> updateHotPosts(Integer categoryId, LocalDate day) {
log.info("카테고리 ID {}의 추천 게시물 업데이트 시작", categoryId);

// 기존 추천 게시물 비활성화
hotPostRepository.deactivateHotPostsByCategory(categoryId);

// 카테고리별 상위 게시물 조회
List<PostListByCategoryDTO> posts = postService.findTopPostsByCategory(categoryId, day, 5);
List<HotPostListByCategoryDTO> updatedHotPosts = posts.stream()
.map(post -> {
HotPostListByCategoryDTO dto = modelMapper.map(post, HotPostListByCategoryDTO.class);
dto.setActive(true); // 새로운 게시물은 활성화
saveNewHotPost(dto);
return dto;
})
.collect(Collectors.toList());
log.info("카테고리 ID {}의 추천 게시물 업데이트 완료, 업데이트된 게시물 수: {}", categoryId, updatedHotPosts.size());

return updatedHotPosts;
}

private void saveNewHotPost(HotPostListByCategoryDTO dto) {
HotPost newHotPost = modelMapper.map(dto, HotPost.class);
newHotPost.activate(); // 새 게시물을 활성화
hotPostRepository.save(newHotPost);
log.info("새로운 추천 게시물 저장: {}", newHotPost);
}

@Transactional(readOnly = true)
@Override
public List<HotPostListByCategoryDTO> getHotPostsByCategory(Integer categoryId, LocalDate day) {
log.info("카테고리 ID {}에 대한 활성화된 추천 게시물 목록 조회를 시작합니다.", categoryId);

// List<HotPost> hotPosts = hotPostRepository.findAllByCategoryIdAndIsActive(categoryId, true);
List<HotPostListByCategoryDTO> hotPosts = hotPostRepository.findHotPostsDetailsByCategory(categoryId);

if (hotPosts.isEmpty()) {
log.warn("카테고리 ID {}에 활성화된 추천 게시물이 없습니다.", categoryId);
} else {
log.info("카테고리 ID {}에 대한 추천 게시물 목록 조회가 완료되었습니다. 게시물 수: {}", categoryId, hotPosts.size());
}

// return hotPosts.stream()
// .map(hotPost -> modelMapper.map(hotPost, HotPostListByCategoryDTO.class))
// .collect(Collectors.toList());
return hotPosts;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.omoknoone.onionhotsayyo.hotpost.vo;

import lombok.Getter;
import lombok.ToString;
import org.omoknoone.onionhotsayyo.hotpost.dto.HotPostListByCategoryDTO;

import java.util.List;


@Getter
@ToString
public class ResponseHotPostListByCategory {

private final List<HotPostListByCategoryDTO> hotCategoryPosts;

public ResponseHotPostListByCategory(List<HotPostListByCategoryDTO> hotCategoryPosts) {
this.hotCategoryPosts = hotCategoryPosts;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,12 @@ public class Member {
@Column
private String profile;
@Column
private String image;
@Column
@CreationTimestamp
private String signUpDate;
@Column(name = "IS_WITHDRAW")
private boolean isWithdraw;

@Column(name = "nationality_id")
private String nationalityId;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,23 @@ public ResponseEntity<?> withdraw(@RequestBody RequestMember member) {
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
}

@GetMapping("/mypage/{memberId}")
public ResponseEntity<ResponseMessage> mypage(@PathVariable String memberId) {

HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "json", Charset.forName("UTF-8")));

ResponseMember responseMember = modelMapper.map(memberService.getMemberDetailsByMemberId(memberId), ResponseMember.class);

Map<String, Object> responseMap = new HashMap<>();
responseMap.put("result", responseMember);

return ResponseEntity
.ok()
.headers(headers)
.body(new ResponseMessage(200, "마이페이지 로딩 성공", responseMap));
}

@GetMapping("/health_check")
public String healthCheck() {
return "Good";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.omoknoone.onionhotsayyo.member.service.AuthService;
import org.omoknoone.onionhotsayyo.member.service.MemberService;
import org.omoknoone.onionhotsayyo.member.vo.RequestLogin;
import org.omoknoone.onionhotsayyo.nationality.service.NationalityService;
import org.springframework.core.env.Environment;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
Expand All @@ -26,13 +27,15 @@
public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

private final MemberService memberService;
private final NationalityService nationalityService;
private final AuthService authService;
private final Environment environment;
private final JwtTokenProvider jwtTokenProvider;

public AuthenticationFilter(AuthenticationManager authenticationManager, MemberService memberService, AuthService authService, Environment environment, JwtTokenProvider jwtTokenProvider) {
public AuthenticationFilter(AuthenticationManager authenticationManager, MemberService memberService, NationalityService nationalityService, AuthService authService, Environment environment, JwtTokenProvider jwtTokenProvider) {
super(authenticationManager);
this.memberService = memberService;
this.nationalityService = nationalityService;
this.authService = authService;
this.environment = environment;
this.jwtTokenProvider = jwtTokenProvider;
Expand Down Expand Up @@ -68,6 +71,7 @@ protected void successfulAuthentication(HttpServletRequest request,
/* 설명. DB를 다녀와 사용자의 고유 아이디(memberId)를 가져올 예정(Principal 객체(Authentication)에는 없는 값이므로) */
MemberDTO memberDetails = memberService.getMemberDetailsByMemberId(id);
String memberId = memberDetails.getMemberId();
String language = nationalityService.viewLanguage(memberDetails.getNationalityId());
// String roleName = memberDetails.getRoleName();

Claims claims = Jwts.claims().setSubject(memberId);
Expand All @@ -84,6 +88,7 @@ protected void successfulAuthentication(HttpServletRequest request,

response.addHeader("accessToken", accessToken);
response.addHeader("memberId", memberId);
response.addHeader("language", language);

/* 설명. refreshToken이 아닌 token의 Id를 전달, refreshToken은 서버만 가지고 있음 */
Cookie cookie = new Cookie("refreshTokenId", refreshTokenId);
Expand Down
Loading