-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
[FEAT] launch / tag 기능 구현
- Loading branch information
Showing
7 changed files
with
330 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
19 changes: 19 additions & 0 deletions
19
src/main/java/com/easyvel/server/launch/LaunchController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.easyvel.server.launch; | ||
|
||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RequestMapping("/launch") | ||
@RestController | ||
public class LaunchController { | ||
|
||
@Value("${easyvel.info.version}") | ||
private String version; | ||
|
||
@GetMapping("/version") | ||
public String getVersion() { | ||
return version; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.easyvel.server.tag; | ||
|
||
import com.easyvel.server.annotation.EasyvelTokenApiImplicitParams; | ||
import com.easyvel.server.config.security.SecurityConfiguration; | ||
import com.easyvel.server.global.dto.PostDto; | ||
import com.easyvel.server.jwt.JwtTokenProvider; | ||
import com.easyvel.server.tag.dto.TagList; | ||
import io.swagger.annotations.ApiOperation; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
|
||
@Slf4j | ||
@RequestMapping("/tags") | ||
@RequiredArgsConstructor | ||
@RestController | ||
public class TagController { | ||
|
||
private final JwtTokenProvider jwtTokenProvider; | ||
private final TagService tagService; | ||
|
||
@ApiOperation("유저 태그 목록") | ||
@EasyvelTokenApiImplicitParams | ||
@GetMapping | ||
public TagList getUserTagList(@RequestHeader(SecurityConfiguration.TOKEN_HEADER) String token) { | ||
String uid = jwtTokenProvider.getUid(token); | ||
|
||
return tagService.getUserTagList(uid); | ||
} | ||
|
||
@ApiOperation("유저 태그 추가") | ||
@EasyvelTokenApiImplicitParams | ||
@PostMapping | ||
public void addUserTag(@RequestBody String tag, | ||
@RequestHeader(SecurityConfiguration.TOKEN_HEADER) String token) { | ||
String uid = jwtTokenProvider.getUid(token); | ||
|
||
tagService.addTag(uid, tag); | ||
} | ||
|
||
@ApiOperation("유저 태그 제거") | ||
@EasyvelTokenApiImplicitParams | ||
@DeleteMapping | ||
public void deleteUserTag(@RequestBody String tag, | ||
@RequestHeader(SecurityConfiguration.TOKEN_HEADER) String token) { | ||
String uid = jwtTokenProvider.getUid(token); | ||
|
||
tagService.deleteTag(uid, tag); | ||
} | ||
|
||
//Todo: 페이징 필요 | ||
@ApiOperation("유저 태그 목록 연관 포스트") | ||
@EasyvelTokenApiImplicitParams | ||
@GetMapping("/posts") | ||
public List<PostDto> getUserTagPostList(@RequestHeader(SecurityConfiguration.TOKEN_HEADER) String token) throws IOException { | ||
String uid = jwtTokenProvider.getUid(token); | ||
|
||
return tagService.getUserTagPostList(uid); | ||
} | ||
|
||
//Todo: 페이징 필요 | ||
@ApiOperation(value = "태그 연관 포스트", notes = "search 파라미터에 검색할 태그를 입력") | ||
@EasyvelTokenApiImplicitParams | ||
@GetMapping("/posts") | ||
public List<PostDto> getUserTagPostList(@RequestParam String search, | ||
@RequestHeader(SecurityConfiguration.TOKEN_HEADER) String token) throws IOException { | ||
String uid = jwtTokenProvider.getUid(token); | ||
|
||
return tagService.getPostDtoListByTag(uid, search); | ||
} | ||
|
||
@ApiOperation(value = "현재 인기있는 태그 목록", notes = "vol 파라미터에 받을 태그 수량을 입력") | ||
@GetMapping("/hot") | ||
public TagList getHotTagList(@RequestParam(defaultValue = "10") int vol) throws IOException { | ||
return tagService.getHotTagList(vol); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.easyvel.server.tag; | ||
|
||
import com.easyvel.server.sign.User; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
import java.util.Optional; | ||
|
||
public interface TagRepository extends JpaRepository<Tag, Long> { | ||
Optional<Tag> findByUser(User user); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
package com.easyvel.server.tag; | ||
|
||
import com.easyvel.server.global.dto.PostDto; | ||
import com.easyvel.server.global.dto.VelogUserInfoDto; | ||
import com.easyvel.server.sign.User; | ||
import com.easyvel.server.sign.UserRepository; | ||
import com.easyvel.server.subscribe.service.SubscribeService; | ||
import com.easyvel.server.tag.dto.TagList; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.jsoup.Jsoup; | ||
import org.jsoup.nodes.Document; | ||
import org.jsoup.nodes.Element; | ||
import org.jsoup.select.Elements; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.io.IOException; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Slf4j | ||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class TagService { | ||
|
||
private final TagRepository tagRepository; | ||
private final UserRepository userRepository; | ||
private final SubscribeService subscribeService; | ||
|
||
public TagList getUserTagList(String uid) { | ||
User user = getUserByUid(uid); | ||
Tag userTag = getUserTag(user); | ||
|
||
return TagList.make(userTag); | ||
} | ||
|
||
public void addTag(String uid, String tag) { | ||
User user = getUserByUid(uid); | ||
Tag userTag = getUserTag(user); | ||
|
||
if (containsTag(userTag, tag)) | ||
throw new IllegalArgumentException("이미 추가한 태그입니다."); | ||
|
||
userTag.getTags().add(tag); | ||
tagRepository.save(userTag); | ||
} | ||
|
||
public void deleteTag(String uid, String tag) { | ||
User user = getUserByUid(uid); | ||
Tag userTag = getUserTag(user); | ||
|
||
if (!containsTag(userTag, tag)) | ||
throw new IllegalArgumentException("목록에 " + tag + "가 없습니다."); | ||
|
||
userTag.getTags().remove(tag); | ||
tagRepository.save(userTag); | ||
} | ||
|
||
/** | ||
* 해당 태그와 관련된 포스트를 크롤링해 PostDto를 만듭니다. | ||
* @param uid | ||
* @param tag | ||
* @return | ||
* @throws IOException | ||
*/ | ||
public List<PostDto> getPostDtoListByTag(String uid, String tag) throws IOException { | ||
List<String> userSubscribeList = getSubscribeNameList(uid); | ||
Elements postsElements = getTagPostsElements(tag); | ||
|
||
return createPostDtoList(userSubscribeList, postsElements); | ||
} | ||
|
||
public List<PostDto> getUserTagPostList(String uid) throws IOException{ | ||
User user = getUserByUid(uid); | ||
Tag userTag = getUserTag(user); | ||
|
||
List<PostDto> postDtoList = new ArrayList<>(); | ||
for (String tag : userTag.getTags()) { | ||
List<PostDto> postDtoListByTag = getPostDtoListByTag(uid, tag); | ||
postDtoList.addAll(postDtoListByTag); | ||
} | ||
postDtoList.sort(PostDto.compareByDate); | ||
|
||
return postDtoList; | ||
} | ||
|
||
/** | ||
* 현재 가장 인기있는 태그 목록을 가져옵니다. | ||
* @param vol 가져올 개수 | ||
* @return | ||
*/ | ||
public TagList getHotTagList(int vol) throws IOException { | ||
String tagUrl = "https://velog.io/tags"; | ||
Document doc = Jsoup.connect(tagUrl).get(); | ||
|
||
return getTagList(vol, doc); | ||
} | ||
|
||
private TagList getTagList(int vol, Document doc) throws IOException { | ||
List<String> tags = new ArrayList<>(); | ||
|
||
Elements elementTags = doc.selectXpath("//*[@id=\"root\"]/div[2]/main/section").select("a"); | ||
int cnt = 0; | ||
for (Element elementTag : elementTags) { | ||
if (cnt >= vol) | ||
break; | ||
|
||
try { | ||
tags.add(elementTag.text()); | ||
} catch (RuntimeException e){ | ||
throw new IOException("문서 구조가 변경되었습니다."); | ||
} | ||
cnt++; | ||
} | ||
|
||
return new TagList(tags); | ||
} | ||
|
||
private List<PostDto> createPostDtoList(List<String> userSubscribeList, Elements postsElements) throws IOException { | ||
List<PostDto> postDtoList = new ArrayList<>(); | ||
for (Element postElement : postsElements) { | ||
try { | ||
PostDto postDto = createPostDto(postElement, userSubscribeList); | ||
postDtoList.add(postDto); | ||
} catch (Exception e){ | ||
throw new IOException("문서 구조가 변경되었습니다."); | ||
} | ||
} | ||
postDtoList.sort(PostDto.compareByDate); | ||
return postDtoList; | ||
} | ||
|
||
//Todo: 이거 subscribe 옮기는게 맞을지..? | ||
private List<String> getSubscribeNameList(String uid) throws IOException { | ||
List<String> userSubscribeList = new ArrayList<>(); | ||
|
||
List<VelogUserInfoDto> subscribers = subscribeService.getSubscribers(uid); | ||
for (VelogUserInfoDto subscriber : subscribers) { | ||
userSubscribeList.add(subscriber.getName()); | ||
} | ||
|
||
return userSubscribeList; | ||
} | ||
|
||
private Elements getTagPostsElements(String tag) throws IOException { | ||
String tagUrl = "https://velog.io/tags/" + tag; | ||
Document doc = Jsoup.connect(tagUrl).get(); | ||
|
||
return doc.select("#root > div > main > div > div").get(1).select("> div"); | ||
} | ||
|
||
//Todo: 전체 적용이 가능한지 알아보기 | ||
private PostDto createPostDto(Element post, List<String> subscribeList) { | ||
PostDto postDto = PostDto.builder() | ||
.name(post.select(".user-info .username a").text()) | ||
.title(post.select("a h2").text()) | ||
.summary(post.select("p").text()) | ||
.date(post.select(".subinfo span").get(0).text()) | ||
.comment(Integer.parseInt(post.select(".subinfo span").get(1).text().replace("개의 댓글", ""))) | ||
.like(Integer.parseInt(post.select(".subinfo span").get(2).text())) | ||
.img(post.select("a div img").attr("src")) | ||
.url("https://velog.io" + post.select("> a").attr("href")) | ||
.build(); | ||
|
||
Elements tags = post.select(".tags-wrapper a"); | ||
for (Element _tag : tags) { | ||
postDto.getTag().add(_tag.text()); | ||
} | ||
|
||
postDto.setSubscribed(subscribeList.contains(postDto.getName())); | ||
|
||
return postDto; | ||
} | ||
|
||
private boolean containsTag(Tag source, String tag) { | ||
return source.getTags().contains(tag); | ||
} | ||
|
||
private Tag getUserTag(User user) { | ||
return tagRepository.findByUser(user) | ||
.orElseThrow(() -> new NullPointerException("user의 Tag 데이터가 NULL 입니다.")); | ||
} | ||
|
||
private User getUserByUid(String uid) { | ||
return userRepository.getByUid(uid) | ||
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 uid 입니다.")); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.easyvel.server.tag.dto; | ||
|
||
import com.easyvel.server.tag.Tag; | ||
import lombok.AllArgsConstructor; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
import lombok.Setter; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
@Getter @Setter | ||
@NoArgsConstructor | ||
@AllArgsConstructor | ||
public class TagList { | ||
List<String> tags; | ||
|
||
public static TagList make(Tag source) { | ||
List<String> tagList = source.getTags(); | ||
List<String> copyList = new ArrayList<>(tagList); | ||
|
||
return new TagList(copyList); | ||
} | ||
} |