Skip to content

Commit cc46fdd

Browse files
authored
Merge pull request #47 from Move-Log/develop
Merge develop to main
2 parents 19ac66b + 6356304 commit cc46fdd

File tree

21 files changed

+329
-90
lines changed

21 files changed

+329
-90
lines changed

src/main/java/com/movelog/domain/news/application/HeadlineGeneratorService.java renamed to src/main/java/com/movelog/domain/news/application/HeadLineGeneratorService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
@Service
1515
@RequiredArgsConstructor
1616
@Slf4j
17-
public class HeadlineGeneratorService {
17+
public class HeadLineGeneratorService {
1818

1919
private final GptService gptService;
2020

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
package com.movelog.domain.news.application;
22

3+
import com.movelog.domain.news.domain.News;
4+
import com.movelog.domain.news.domain.repository.NewsRepository;
5+
import com.movelog.domain.news.dto.request.CreateNewsReq;
36
import com.movelog.domain.news.dto.request.NewsHeadLineReq;
47
import com.movelog.domain.news.dto.response.HeadLineRes;
8+
import com.movelog.domain.record.domain.Keyword;
9+
import com.movelog.domain.record.domain.VerbType;
10+
import com.movelog.domain.record.exception.KeywordNotFoundException;
11+
import com.movelog.domain.record.repository.KeywordRepository;
512
import com.movelog.domain.user.application.UserService;
613
import com.movelog.domain.user.domain.User;
714
import com.movelog.domain.user.domain.repository.UserRepository;
815
import com.movelog.domain.user.exception.UserNotFoundException;
9-
import com.movelog.global.DefaultAssert;
1016
import com.movelog.global.config.security.token.UserPrincipal;
17+
import com.movelog.global.util.S3Util;
1118
import lombok.RequiredArgsConstructor;
12-
import org.springframework.http.ResponseEntity;
1319
import org.springframework.stereotype.Service;
1420
import org.springframework.transaction.annotation.Transactional;
21+
import org.springframework.web.multipart.MultipartFile;
1522

1623
import java.util.List;
1724
import java.util.Optional;
@@ -20,24 +27,58 @@
2027
@RequiredArgsConstructor
2128
@Transactional(readOnly = true)
2229
public class NewsService {
23-
private final HeadlineGeneratorService headlineGeneratorService;
30+
private final HeadLineGeneratorService headLineGeneratorService;
2431
private final UserService userService;
25-
private final UserRepository userRepository;
32+
private final KeywordRepository keywordRepository;
33+
private final NewsRepository newsRepository;
34+
private final S3Util s3Util;
2635

27-
public List<HeadLineRes> createHeadLine(UserPrincipal userPrincipal, NewsHeadLineReq newsHeadLineReq) {
36+
public List<HeadLineRes> createHeadLine(UserPrincipal userPrincipal, Long keywordId, NewsHeadLineReq newsHeadLineReq) {
2837
User user = validateUser(userPrincipal);
2938
// id가 5인 유저 정보(테스트용)
3039
// User user = userRepository.findById(5L).orElseThrow(UserNotFoundException::new);
40+
Keyword keyword = validateKeyword(keywordId);
3141
String option = newsHeadLineReq.getOption();
32-
String verb = newsHeadLineReq.getVerb();
33-
String noun = newsHeadLineReq.getNoun();
34-
return headlineGeneratorService.generateHeadLine(option, verb, noun);
42+
String verb = VerbType.getStringVerbType(keyword.getVerbType());
43+
String noun = keyword.getKeyword();
44+
45+
return headLineGeneratorService.generateHeadLine(option, verb, noun);
3546
}
3647

48+
@Transactional
49+
public void createNews(UserPrincipal userPrincipal, Long keywordId, CreateNewsReq createNewsReq, MultipartFile img) {
50+
User user = validateUser(userPrincipal);
51+
// id가 5인 유저 정보(테스트용)
52+
// User user = userRepository.findById(5L).orElseThrow(UserNotFoundException::new);
53+
Keyword keyword = validateKeyword(keywordId);
54+
55+
String newsImgUrl = s3Util.uploadToNewsFolder(img);
56+
57+
News news = News.builder()
58+
.headLine(createNewsReq.getHeadLine())
59+
.newsUrl(newsImgUrl)
60+
.keyword(keyword)
61+
.build();
3762

63+
newsRepository.save(news);
64+
65+
}
66+
67+
68+
69+
// User 정보 검증
3870
private User validateUser(UserPrincipal userPrincipal) {
3971
Optional<User> userOptional = userService.findById(userPrincipal.getId());
40-
DefaultAssert.isOptionalPresent(userOptional);
72+
if (userOptional.isEmpty()) { throw new UserNotFoundException(); }
4173
return userOptional.get();
4274
}
75+
76+
// Keyword 정보 검증
77+
private Keyword validateKeyword(Long keywordId) {
78+
Optional<Keyword> keywordOptional = keywordRepository.findById(keywordId);
79+
if (keywordOptional.isEmpty()) { throw new KeywordNotFoundException(); }
80+
return keywordOptional.get();
81+
}
82+
83+
4384
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.movelog.domain.news.domain;
2+
3+
import com.movelog.domain.common.BaseEntity;
4+
import com.movelog.domain.record.domain.Keyword;
5+
import jakarta.persistence.*;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import lombok.NoArgsConstructor;
9+
10+
@Entity
11+
@Table(name = "News")
12+
@NoArgsConstructor
13+
@Getter
14+
public class News extends BaseEntity {
15+
16+
@Id
17+
@GeneratedValue(strategy = GenerationType.IDENTITY)
18+
@Column(name = "news_id", updatable = false)
19+
private Long newsId;
20+
21+
private String headLine; //뉴스 헤드라인
22+
23+
private String newsUrl; //뉴스 URL
24+
25+
@ManyToOne(fetch = FetchType.LAZY)
26+
@JoinColumn(name = "keyword_id")
27+
private Keyword keyword;
28+
29+
@Builder
30+
public News(String headLine, String newsUrl, Keyword keyword) {
31+
this.headLine = headLine;
32+
this.newsUrl = newsUrl;
33+
this.keyword = keyword;
34+
}
35+
36+
37+
38+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.movelog.domain.news.domain.repository;
2+
3+
import com.movelog.domain.news.domain.News;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.stereotype.Repository;
6+
7+
@Repository
8+
public interface NewsRepository extends JpaRepository<News, Long> {
9+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.movelog.domain.news.dto.request;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
@Builder
10+
@AllArgsConstructor
11+
@NoArgsConstructor
12+
@Getter
13+
public class CreateNewsReq {
14+
15+
@Schema( type = "String", example ="5년 만의 첫 도전, 무엇이 그를 움직이게 했나?", description="사용자가 선택한/생성한 헤드라인 정보입니다.")
16+
private String headLine;
17+
18+
}

src/main/java/com/movelog/domain/news/dto/request/NewsHeadLineReq.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,4 @@
1313
public class NewsHeadLineReq {
1414
@Schema( type = "String", example ="첫 도전, 오랜만에 다시, 꾸준히 이어온 기록, 끊어낸 습관 중 택 1", description="뉴스 헤드라인 고정 옵션입니다.")
1515
private String option;
16-
17-
@Schema( type = "String", example ="했어요, 먹었어요, 갔어요 중 택 1", description="사용자가 선택한 동사 정보입니다.")
18-
private String verb;
19-
20-
@Schema( type = "String", example ="클라이밍", description="사용자가 선택한 명사 정보입니다.")
21-
private String noun;
22-
23-
2416
}
Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
package com.movelog.domain.news.presentation;
22

33
import com.movelog.domain.news.application.NewsService;
4+
import com.movelog.domain.news.dto.request.CreateNewsReq;
45
import com.movelog.domain.news.dto.request.NewsHeadLineReq;
56
import com.movelog.domain.news.dto.response.HeadLineRes;
67
import com.movelog.global.config.security.token.CurrentUser;
78
import com.movelog.global.config.security.token.UserPrincipal;
9+
import com.movelog.global.payload.Message;
10+
import com.movelog.global.util.ApiResponseUtil;
811
import io.swagger.v3.oas.annotations.Operation;
912
import io.swagger.v3.oas.annotations.Parameter;
13+
import io.swagger.v3.oas.annotations.media.Content;
14+
import io.swagger.v3.oas.annotations.media.Schema;
1015
import io.swagger.v3.oas.annotations.responses.ApiResponse;
16+
1117
import io.swagger.v3.oas.annotations.responses.ApiResponses;
1218
import io.swagger.v3.oas.annotations.tags.Tag;
1319
import lombok.RequiredArgsConstructor;
1420
import lombok.extern.slf4j.Slf4j;
15-
import org.springframework.web.bind.annotation.PostMapping;
16-
import org.springframework.web.bind.annotation.RequestBody;
17-
import org.springframework.web.bind.annotation.RequestMapping;
18-
import org.springframework.web.bind.annotation.RestController;
21+
import org.springframework.http.ResponseEntity;
22+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
23+
import org.springframework.web.ErrorResponse;
24+
import org.springframework.web.bind.annotation.*;
25+
import org.springframework.web.multipart.MultipartFile;
1926

2027
import java.util.List;
2128

@@ -30,17 +37,43 @@ public class NewsController {
3037

3138
@Operation(summary = "뉴스 헤드라인 생성 API", description = "뉴스 헤드라인을 생성하는 API입니다.")
3239
@ApiResponses(value = {
33-
@ApiResponse(responseCode = "200", description = "뉴스 헤드라인 생성 성공"),
34-
@ApiResponse(responseCode = "400", description = "뉴스 헤드라인 생성 실패")
40+
@ApiResponse(responseCode = "200", description = "뉴스 헤드라인 생성 성공",
41+
content = @Content(mediaType = "application/json", schema = @Schema(implementation = List.class))),
42+
@ApiResponse(responseCode = "400", description = "뉴스 헤드라인 생성 실패",
43+
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
3544
})
36-
@PostMapping("/headline")
37-
public List<HeadLineRes> createHeadLine(
38-
@Parameter(description = "Access Token을 입력해주세요.", required = true) @CurrentUser UserPrincipal userPrincipal,
45+
@PostMapping("/{keywordId}/headline")
46+
public ResponseEntity<?> createHeadLine(
47+
@Parameter(description = "Access Token을 입력해주세요.", required = true) @AuthenticationPrincipal UserPrincipal userPrincipal,
48+
@Parameter(description = "키워드 ID(동사-명사 쌍에 대한 ID)를 입력해주세요.", required = true) @PathVariable Long keywordId,
3949
@Parameter(description = "뉴스 헤드라인 생성 요청", required = true) @RequestBody NewsHeadLineReq newsHeadLineReq
4050
) {
41-
return newsService.createHeadLine(userPrincipal, newsHeadLineReq);
51+
List<HeadLineRes> response = newsService.createHeadLine(userPrincipal, keywordId, newsHeadLineReq);
52+
return ResponseEntity.ok(ApiResponseUtil.success(response));
4253
}
4354

4455

56+
@Operation(summary = "뉴스 생성 및 저장 API(기존 이미지 기록 기반)", description = "사용자의 기존 기록 이미지로 뉴스를 생성합니다.")
57+
@ApiResponses(value = {
58+
@ApiResponse(responseCode = "200", description = "뉴스 생성 및 저장 성공",
59+
content = @Content(mediaType = "application/json", schema = @Schema(implementation = Message.class))),
60+
@ApiResponse(responseCode = "400", description = "뉴스 생성 및 저장 실패",
61+
content = @Content(mediaType = "application/json", schema = @Schema(implementation = ErrorResponse.class)))
62+
})
63+
@PostMapping("/{keywordId}")
64+
public ResponseEntity<?> createNews(
65+
@Parameter(description = "Access Token을 입력해주세요.", required = true) @AuthenticationPrincipal UserPrincipal userPrincipal,
66+
@Parameter(description = "키워드 ID(동사-명사 쌍에 대한 ID)를 입력해주세요.", required = true) @PathVariable Long keywordId,
67+
@Parameter(description = "뉴스 생성 및 저장을 위한 정보를 입력해주세요", required = true) @RequestPart CreateNewsReq createNewsReq,
68+
@Parameter(description = "뉴스 이미지를 파일 형식으로 입력해주세요", required = true) @RequestParam(value = "img", required = false) MultipartFile img
69+
) {
70+
newsService.createNews(userPrincipal, keywordId, createNewsReq, img);
71+
return ResponseEntity.ok(ApiResponseUtil.success(Message.builder().message("뉴스가 생성되었습니다.").build()));
72+
}
73+
74+
75+
76+
77+
4578

4679
}

src/main/java/com/movelog/domain/record/domain/Keyword.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.movelog.domain.record.domain;
22

33
import com.movelog.domain.common.BaseEntity;
4+
import com.movelog.domain.news.domain.News;
5+
import com.movelog.domain.user.domain.User;
46
import jakarta.persistence.*;
57
import lombok.Builder;
68
import lombok.Getter;
@@ -20,13 +22,26 @@ public class Keyword extends BaseEntity {
2022
@Column(name = "keyword_id", updatable = false)
2123
private Long keywordId;
2224

23-
private String keyword;
25+
private String keyword; //명사
26+
27+
@Enumerated(EnumType.STRING) // Enum을 문자열로 저장
28+
@Column(name = "verb_type")
29+
private VerbType verbType;
30+
31+
@ManyToOne(fetch = FetchType.LAZY)
32+
@JoinColumn(name = "user_id")
33+
private User user;
2434

2535
@OneToMany(mappedBy = "keyword")
2636
private List<Record> records = new ArrayList<>();
2737

38+
@OneToMany(mappedBy = "keyword")
39+
private List<News> news = new ArrayList<>();
40+
2841
@Builder
29-
public Keyword(String keyword) {
42+
public Keyword(User user, String keyword, VerbType verbType) {
43+
this.user = user;
3044
this.keyword = keyword;
45+
this.verbType = verbType;
3146
}
3247
}

src/main/java/com/movelog/domain/record/domain/Record.java

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,19 @@ public class Record extends BaseEntity {
2020
@Column(name = "record_id", updatable = false)
2121
private Long recordId;
2222

23-
@ManyToOne(fetch = FetchType.LAZY)
24-
@JoinColumn(name = "user_id")
25-
private User user;
26-
2723
@ManyToOne(fetch = FetchType.LAZY)
2824
@JoinColumn(name = "keyword_id")
2925
private Keyword keyword;
3026

31-
@Enumerated(EnumType.STRING) // Enum을 문자열로 저장
32-
private VerbType verbType;
33-
3427
@Column(name = "record_image")
3528
private String recordImage;
3629

3730
@Column(name = "action_time")
3831
private java.time.LocalDateTime actionTime;
3932

4033
@Builder
41-
public Record(User user, Keyword keyword, VerbType verbType, String recordImage) {
42-
this.user = user;
34+
public Record(Keyword keyword, String recordImage) {
4335
this.keyword = keyword;
44-
this.verbType = verbType;
4536
this.recordImage = recordImage;
4637
this.actionTime = actionTime == null? LocalDateTime.now():actionTime;
4738
}

src/main/java/com/movelog/domain/record/domain/VerbType.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,9 @@ public static VerbType fromValue(String value) {
2222
}
2323
throw new IllegalArgumentException("No enum constant for value: " + value);
2424
}
25+
26+
public static String getStringVerbType(VerbType verbType) {
27+
return verbType.getVerbType();
28+
}
29+
2530
}

0 commit comments

Comments
 (0)