Skip to content

Commit

Permalink
Merge pull request #20 from 7th-UMC-Hackathon-TeamV/feat/imageUpload
Browse files Browse the repository at this point in the history
feat: 이미지 업로드 기능
  • Loading branch information
dokyung-kang authored Jan 11, 2025
2 parents f8dac76 + bef96cf commit 8c5eaed
Show file tree
Hide file tree
Showing 10 changed files with 80 additions and 255 deletions.
11 changes: 7 additions & 4 deletions src/main/java/banban/springboot/config/AmazonConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,16 @@ public class AmazonConfig {
@Value("${cloud.aws.region.static}")
private String region;

@Value(("${cloud.aws.s3.bucket}"))
@Value("${cloud.aws.s3.bucket}")
private String bucket;

@Value("${cloud.aws.s3.path.news}")
private String newsPath;

@PostConstruct
public void init() {this.awsCredentials = new BasicAWSCredentials(accessKey, secretKey);}
public void init() {
this.awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
}

@Bean
public AmazonS3 amazonS3() {
Expand All @@ -47,6 +49,7 @@ public AmazonS3 amazonS3() {
}

@Bean
public AWSCredentialsProvider awsCredentialsProvider() {return new AWSStaticCredentialsProvider(awsCredentials);}

public AWSCredentialsProvider awsCredentialsProvider() {
return new AWSStaticCredentialsProvider(awsCredentials);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package banban.springboot.converter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;

import java.lang.reflect.Type;

@Component
public class MultipartJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {

/** "Content-Type: multipart/form-data" 헤더를 지원하는 HTTP 요청 변환기 */
public MultipartJackson2HttpMessageConverter(ObjectMapper objectMapper) {
super(objectMapper, MediaType.APPLICATION_OCTET_STREAM);
}

@Override
public boolean canWrite(Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
public boolean canWrite(Type type, Class<?> clazz, MediaType mediaType) {
return false;
}

@Override
protected boolean canWrite(MediaType mediaType) {
return false;
}
}
6 changes: 3 additions & 3 deletions src/main/java/banban/springboot/domain/entity/News.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ public class News {
@Column(name = "like_count", nullable = false)
private Integer likes;

@ElementCollection
private List<String> thumbnail_URL;
// @Column(nullable = false)
private String thumbnail_URL;

@Column(nullable = false)
private boolean isBreakingNews;

@Column(nullable = false)
private LocalDateTime createdAt;
private LocalDateTime createdAt = LocalDateTime.now();

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

Expand All @@ -28,10 +27,5 @@ public interface NewsRepository extends JpaRepository<News,Long> {

// 특정 그룹의 일반 뉴스 조회
List<News> findByTeamGroupAndIsBreakingNewsFalse(TeamGroup teamGroup);

List<News> findByTeamGroupAndCreatedAtBetween(TeamGroup teamGroup, LocalDateTime start, LocalDateTime end);
void deleteByCreatedAtBefore(LocalDateTime dateTime);


}

9 changes: 7 additions & 2 deletions src/main/java/banban/springboot/s3/AmazonS3Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
@Slf4j
@Component
@RequiredArgsConstructor
public class AmazonS3Manager {
public class AmazonS3Manager{

private final AmazonS3 amazonS3;

Expand All @@ -24,13 +24,18 @@ public class AmazonS3Manager {

public String uploadFile(String keyName, MultipartFile file){
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentType(file.getContentType());
metadata.setContentLength(file.getSize());
try {
amazonS3.putObject(new PutObjectRequest(amazonConfig.getBucket(), keyName, file.getInputStream(), metadata));
} catch (IOException e){
}catch (IOException e){
log.error("error at AmazonS3Manager uploadFile : {}", (Object) e.getStackTrace());
}

return amazonS3.getUrl(amazonConfig.getBucket(), keyName).toString();
}

public String generateNewsKeyName(Uuid uuid) {
return amazonConfig.getNewsPath() + '/' + uuid.getUuid();
}
}
121 changes: 17 additions & 104 deletions src/main/java/banban/springboot/service/NewsService.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,18 @@
import banban.springboot.repository.GroupRepository;
import banban.springboot.repository.MemberRepository;
import banban.springboot.repository.NewsRepository;
import banban.springboot.web.controller.NewsTestController;
import banban.springboot.s3.AmazonS3Manager;
import banban.springboot.s3.Uuid;
import banban.springboot.s3.UuidRepository;
import banban.springboot.web.dto.request.NewsRequestDTO;
import banban.springboot.web.dto.response.NewsResponseDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
Expand All @@ -33,17 +29,25 @@ public class NewsService {
private final NewsRepository newsRepository;
private final MemberRepository memberRepository;
private final GroupRepository groupRepository;
//private final Environment environment;
private final AmazonS3Manager s3Manager;
private final UuidRepository uuidRepository;

@Transactional
public NewsResponseDTO.NewsCreateResponseDTO createNews(String groupKey, Long memberId, NewsRequestDTO newsRequestDTO) {
public NewsResponseDTO.NewsCreateResponseDTO createNews(String groupKey, Long memberId, NewsRequestDTO newsRequestDTO, MultipartFile thumbnail_img) {

TeamGroup teamGroup = groupRepository.findByGroupKey(groupKey)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAMGROUP_NOT_FOUND));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

String uuid = UUID.randomUUID().toString();
Uuid savedUuid = uuidRepository.save(Uuid.builder()
.uuid(uuid).build());

String pictureUrl = s3Manager.uploadFile(s3Manager.generateNewsKeyName(savedUuid), thumbnail_img);


News news = News.builder()

.teamGroup(teamGroup)
Expand All @@ -53,8 +57,8 @@ public NewsResponseDTO.NewsCreateResponseDTO createNews(String groupKey, Long me
.isBreakingNews(newsRequestDTO.isBreakingNews())
.likes(0)
.newsCategories(newsRequestDTO.getNewsCategories())
//.createdAt(newsRequestDTO.getCreatedAt())
.createdAt(getCurrentTime())
.createdAt(newsRequestDTO.getCreatedAt())
.thumbnail_URL(pictureUrl)
.build();

news = newsRepository.save(news);
Expand Down Expand Up @@ -135,95 +139,4 @@ public List<NewsResponseDTO.NewsReadResponseDTO> getRegularNewsByGroupKey(String
.map(NewsResponseDTO.NewsReadResponseDTO::from)
.toList();
}

// private LocalDateTime getCurrentTime() {
// if (isTestMode()) {
// return NewsTestController.getMockCurrentTime();
// }
// return LocalDateTime.now();
// }

// private boolean isTestMode() {
// return Arrays.asList(environment.getActiveProfiles()).contains("test");
// }

private LocalDateTime getCurrentTime() {
return NewsTestController.getMockCurrentTime(); // 항상 테스트 시간 반환
}

public List<NewsResponseDTO.NewsTodayResponseDTO> getTodayNews(String groupKey, Long memberId) {
// 그룹과 멤버 존재 확인
TeamGroup teamGroup = groupRepository.findByGroupKey(groupKey)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAMGROUP_NOT_FOUND));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

//LocalDateTime now = LocalDateTime.now();
LocalDateTime now = getCurrentTime();
// LocalDateTime startOfDay = now.toLocalDate().atStartOfDay();
LocalDateTime startOfDay = now.minusDays(1).withHour(18).withMinute(0).withSecond(0);
LocalDateTime endOfToday = now.toLocalDate().atTime(17, 59, 59);

List<News> todayNews = newsRepository.findByTeamGroupAndCreatedAtBetween(
teamGroup, startOfDay, endOfToday);

return todayNews.stream()
.map(NewsResponseDTO.NewsTodayResponseDTO::from)
.collect(Collectors.toList());
}

public List<NewsResponseDTO.NewsYesterdayResponseDTO> getYesterdayNews(String groupKey, Long memberId) {
// 그룹과 멤버 존재 확인
TeamGroup teamGroup = groupRepository.findByGroupKey(groupKey)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAMGROUP_NOT_FOUND));

Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GeneralException(ErrorStatus.MEMBER_NOT_FOUND));

//LocalDateTime now = LocalDateTime.now();
LocalDateTime now = getCurrentTime();
LocalDateTime twoYesterdayAt6PM = now.minusDays(2).withHour(18).withMinute(0).withSecond(0);
LocalDateTime yesterdayAt6PM = now.minusDays(1).withHour(17).withMinute(59).withSecond(59);

List<News> yesterdayNews = newsRepository.findByTeamGroupAndCreatedAtBetween(
teamGroup, twoYesterdayAt6PM, yesterdayAt6PM);

return yesterdayNews.stream()
.map(NewsResponseDTO.NewsYesterdayResponseDTO::from)
.collect(Collectors.toList());
}

@Scheduled(cron = "0 0 18 * * *")
@Transactional
public void deleteOldNews() {
LocalDateTime now = getCurrentTime(); // 수정된 부분
LocalDateTime yesterdayAt6PM = now.minusDays(1)
.withHour(18).withMinute(0).withSecond(0);
newsRepository.deleteByCreatedAtBefore(yesterdayAt6PM);
}

/***
* 어제 18시부터 작성한 글에서 오늘 18시전까지 작성한 글 목록 조회
*/
public List<NewsResponseDTO.NewsReadResponseDTO> getNewsBetweenYesterday18ToToday18(String groupKey) {
// 그룹 확인
TeamGroup teamGroup = groupRepository.findByGroupKey(groupKey)
.orElseThrow(() -> new GeneralException(ErrorStatus.TEAMGROUP_NOT_FOUND));

// 현재 시간
LocalDateTime now = getCurrentTime();

// 어제 18시부터 오늘 18시전까지
LocalDateTime yesterdayAt6PM = now.minusDays(1).withHour(18).withMinute(0).withSecond(0);
LocalDateTime todayAt6PM = now.withHour(17).withMinute(59).withSecond(59);

// 뉴스 조회
List<News> newsList = newsRepository.findByTeamGroupAndCreatedAtBetween(teamGroup, yesterdayAt6PM, todayAt6PM);

// 뉴스 -> DTO 변환
return newsList.stream()
.map(NewsResponseDTO.NewsReadResponseDTO::from)
.toList();
}
}
48 changes: 8 additions & 40 deletions src/main/java/banban/springboot/web/controller/NewsController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,17 @@
import banban.springboot.web.dto.response.NewsResponseDTO;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

Expand All @@ -28,9 +31,11 @@ public class NewsController {
private final NewsService newsService;

@Operation(summary = "뉴스 생성")
@PostMapping("/{groupKey}/users/news/{memberId}")
public ApiResponse<NewsResponseDTO.NewsCreateResponseDTO> createNews(@PathVariable String groupKey, @PathVariable Long memberId, @Valid @RequestBody NewsRequestDTO newsRequestDTO) {
NewsResponseDTO.NewsCreateResponseDTO news = newsService.createNews(groupKey, memberId, newsRequestDTO);
@PostMapping(value = "/{groupKey}/users/news/{memberId}", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public ApiResponse<NewsResponseDTO.NewsCreateResponseDTO> createNews(@PathVariable String groupKey, @PathVariable Long memberId, @Parameter(content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE))
@Valid @RequestPart("newsRequestDTO") NewsRequestDTO newsRequestDTO,
@RequestPart("thumbnail_img") MultipartFile thumbnail_img) {
NewsResponseDTO.NewsCreateResponseDTO news = newsService.createNews(groupKey, memberId, newsRequestDTO, thumbnail_img);
return ApiResponse.onSuccess(news);
}
// 뉴스 공감 누르기
Expand Down Expand Up @@ -99,41 +104,4 @@ public ApiResponse<List<NewsResponseDTO.NewsReadResponseDTO>> getRegularNewsByGr
List<NewsResponseDTO.NewsReadResponseDTO> regularNews = newsService.getRegularNewsByGroupKey(groupKey);
return ApiResponse.onSuccess(regularNews);
}

@GetMapping("/{groupKey}/users/news/today/{memberId}")
@Operation(summary = "오늘 공개 예정 뉴스글 조회")
@Parameters({
@Parameter(name = "groupKey", description = "그룹 키"),
@Parameter(name = "memberId", description = "멤버 ID")
})
public ApiResponse<List<NewsResponseDTO.NewsTodayResponseDTO>> getTodayNews(
@PathVariable String groupKey,
@PathVariable Long memberId) {
List<NewsResponseDTO.NewsTodayResponseDTO> todayNews = newsService.getTodayNews(groupKey, memberId);
return ApiResponse.onSuccess(todayNews);
}

@GetMapping("/{groupKey}/users/news/yesterday/{memberId}")
@Operation(summary = "어제 공개된 뉴스글 조회")
@Parameters({
@Parameter(name = "groupKey", description = "그룹 키"),
@Parameter(name = "memberId", description = "멤버 ID")
})
public ApiResponse<List<NewsResponseDTO.NewsYesterdayResponseDTO>> getYesterdayNews(
@PathVariable String groupKey,
@PathVariable Long memberId) {
List<NewsResponseDTO.NewsYesterdayResponseDTO> yesterdayNews = newsService.getYesterdayNews(groupKey, memberId);
return ApiResponse.onSuccess(yesterdayNews);
}

@GetMapping("/{groupKey}/news/yesterday-today")
@Operation(summary = "어제 18시부터 오늘 18시 전까지 뉴스 조회")
@Parameters({
@Parameter(name = "groupKey", description = "그룹 키")
})
public ApiResponse<List<NewsResponseDTO.NewsReadResponseDTO>> getNewsBetweenYesterday18ToToday18(
@PathVariable String groupKey) {
List<NewsResponseDTO.NewsReadResponseDTO> newsList = newsService.getNewsBetweenYesterday18ToToday18(groupKey);
return ApiResponse.onSuccess(newsList);
}
}
Loading

0 comments on commit 8c5eaed

Please sign in to comment.