diff --git a/build.gradle b/build.gradle index 0d36e87..485d20c 100644 --- a/build.gradle +++ b/build.gradle @@ -55,6 +55,10 @@ dependencies { // implementation group: 'commons-io', name: 'commons-io', version: '2.9.0' implementation 'com.google.guava:guava:33.1.0-jre' + //s3 + implementation 'software.amazon.awssdk:s3:2.20.0' + implementation 'software.amazon.awssdk:sts:2.20.0' + } tasks.named('test') { diff --git a/src/main/java/com/example/healthylife/config/S3Config.java b/src/main/java/com/example/healthylife/config/S3Config.java new file mode 100644 index 0000000..85b51b0 --- /dev/null +++ b/src/main/java/com/example/healthylife/config/S3Config.java @@ -0,0 +1,18 @@ +package com.example.healthylife.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; +import software.amazon.awssdk.regions.Region; +import software.amazon.awssdk.services.s3.S3Client; + +@Configuration +public class S3Config { + @Bean + public S3Client s3Client() { + return S3Client.builder() + .region(Region.AP_NORTHEAST_2) // 여러분의 AWS S3 리전으로 변경 + .credentialsProvider(DefaultCredentialsProvider.create()) + .build(); + } +} diff --git a/src/main/java/com/example/healthylife/controller/HeartController.java b/src/main/java/com/example/healthylife/controller/HeartController.java new file mode 100644 index 0000000..4333547 --- /dev/null +++ b/src/main/java/com/example/healthylife/controller/HeartController.java @@ -0,0 +1,47 @@ +package com.example.healthylife.controller; + +import com.example.healthylife.entity.TodayEntity; +import com.example.healthylife.entity.UserEntity; +import com.example.healthylife.service.HeartService; +import com.example.healthylife.service.TodayService; +import com.example.healthylife.service.UserService; +import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Optional; + +@RestController +@RequestMapping("/hearts") +public class HeartController { + + @Autowired + private HeartService heartService; + + @Autowired + private UserService userService; + + @Autowired + private TodayService todayService; + + @ApiOperation("하트") + @PostMapping("/heart/{todaySq}") + public void like(@PathVariable("todaySq") long todaySq, @RequestParam String userId) { + Optional user = userService.findUserById(userId); + Optional today = todayService.findbytodaysq(todaySq); + + if (user.isPresent() && today.isPresent()) { + heartService.Like(user.get(), today.get()); + } else { + // Handle the case when user or today entity is not found + throw new RuntimeException("User or Today entity not found"); + } + } + + @ApiOperation("하트 수") + @GetMapping("/count/{todaySq}") + public Long heartCount(@PathVariable("todaySq") long todaySq) { + Optional today = todayService.findbytodaysq(todaySq); + return heartService.HeartCount(today.orElse(null)); + } +} diff --git a/src/main/java/com/example/healthylife/controller/TodayController.java b/src/main/java/com/example/healthylife/controller/TodayController.java index 60b87ce..47f40d3 100644 --- a/src/main/java/com/example/healthylife/controller/TodayController.java +++ b/src/main/java/com/example/healthylife/controller/TodayController.java @@ -1,18 +1,29 @@ package com.example.healthylife.controller; +import com.example.healthylife.config.jwt.JwtUtil; +import com.example.healthylife.entity.CommunityEntity; import com.example.healthylife.entity.TodayEntity; +import com.example.healthylife.entity.UserEntity; +import com.example.healthylife.repository.UserRepository; import com.example.healthylife.service.TodayService; import io.swagger.annotations.ApiOperation; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; import java.util.List; +import java.util.Optional; @RequestMapping("/today") @RestController @RequiredArgsConstructor public class TodayController { private final TodayService todayService; + private final JwtUtil jwtUtil; + private final UserRepository userRepository; @ApiOperation(value = "오운완 전체조회") @GetMapping("/all") @@ -24,28 +35,73 @@ public List todayList(){ //오운완 내가 쓴 글 조회 @ApiOperation(value = "오운완 내가쓴 글 조회") @GetMapping("/myTodayContents") - public List myTodayContents(@RequestParam String userId){ - return todayService.findMyTodayContents(userId); + public ResponseEntity> myTodayContents(@RequestHeader("Authorization") String authorizationHeader){ + String jwtToken = jwtUtil.extractTokenFromHeader(authorizationHeader); + if (jwtToken == null || !jwtUtil.validateToken(jwtToken)) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + String userId = jwtUtil.getUserId(jwtToken); + List todayEntities = todayService.findMyTodayContents(userId); + if (todayEntities.isEmpty()) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); + } + return ResponseEntity.ok(todayEntities); //확실하게 리스트에 담고싶으면 그래도됨 } - @ApiOperation(value = "오운완 글작성") +/* @ApiOperation(value = "오운완 글작성") @PostMapping("/register") public TodayEntity register (@RequestBody TodayEntity todayEntity){ return todayService.registerToday(todayEntity); - } + }*/ + + @ApiOperation(value = "오운완 글 수정") + @PutMapping("/update") + public ResponseEntity update(@RequestBody TodayEntity updatedTodayEntity, Authentication authentication) { + String username = authentication.getName(); // 인증된 사용자의 이름 (ID) - @ApiOperation(value = "오운완 글수정") - @PostMapping("/update") - public TodayEntity update(@RequestBody TodayEntity todayEntity){ - return todayService.updateEntity(todayEntity); + try { + TodayEntity updatedEntity = todayService.updateEntity(updatedTodayEntity.getTodaySq(),updatedTodayEntity, username); + return ResponseEntity.ok(updatedEntity); + } catch (SecurityException e) { + return ResponseEntity.status(HttpStatus.FORBIDDEN).build(); // 403 Forbidden 응답 + } catch (RuntimeException e) { + return ResponseEntity.status(HttpStatus.NOT_FOUND).build(); // 404 Not Found 응답 + } } - @ApiOperation(value = "오운완 글삭제") - @PostMapping("/delete") - public Boolean delete(@RequestParam long todaySq){ + @ApiOperation(value = "오운완 글 삭제") + @DeleteMapping("/delete/{todaySq}") + public ResponseEntity delete(@PathVariable("todaySq") Long todaySq, Authentication authentication) { + String username = authentication.getName(); // 인증된 사용자의 이름 (ID) + UserEntity user = userRepository.findByUserId(username) + .orElseThrow(() -> new RuntimeException("유저가 없습니다.")); + // 권한 체크 또는 작성자 확인 로직 추가 todayService.deleteByTodaySq(todaySq); - return true; + return ResponseEntity.noContent().build(); } + + @ApiOperation(value = "오운완 상세보기") + @GetMapping("/todayDetail/{todaysq}") + public ResponseEntity todaysq(@PathVariable("todaysq") Long todaysq) { + Optional todays = todayService.findTodayBySq(todaysq); + return todays.map(ResponseEntity::ok).orElseGet(() -> ResponseEntity.status(HttpStatus.NOT_FOUND).build()); + } + + @ApiOperation(value = "투데이 글 작성") + @PostMapping("/create") + public ResponseEntity createTodayPost(@RequestParam("content") String content, + @RequestPart("file") MultipartFile file, + Authentication authentication) { + String username = authentication.getName(); + UserEntity user = userRepository.findByUserId(username) + .orElseThrow(() -> new RuntimeException("유저가 없습니다.")); + + // 투데이 글 작성 + TodayEntity todayPost = todayService.createTodayPost(content, file, user); + return ResponseEntity.status(HttpStatus.CREATED).body(todayPost); + } + + } diff --git a/src/main/java/com/example/healthylife/entity/HeartEntity.java b/src/main/java/com/example/healthylife/entity/HeartEntity.java new file mode 100644 index 0000000..39f1a39 --- /dev/null +++ b/src/main/java/com/example/healthylife/entity/HeartEntity.java @@ -0,0 +1,44 @@ +package com.example.healthylife.entity; + +import lombok.*; + +import javax.persistence.*; +import java.time.LocalDateTime; + + +@ToString +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Entity +@Table(name = "heart") +public class HeartEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "heart_sq", unique = true, nullable = false) + private Long heartSq; + + @ManyToOne + @JoinColumn(name = "user_sq", nullable = false) + private UserEntity user; + + @ManyToOne + @JoinColumn(name = "today_sq", nullable = false) + private TodayEntity today; + + @Column(name = "status", nullable = false) + private Boolean status; + + @Column(name = "created_at", nullable = false) + private LocalDateTime createdAt = LocalDateTime.now(); + + + // 상태를 토글하는 메서드 + public void toggleStatus() { + this.status = !this.status; + } + +} diff --git a/src/main/java/com/example/healthylife/entity/TodayCommentsEntity.java b/src/main/java/com/example/healthylife/entity/TodayCommentsEntity.java index b1637c5..5458633 100644 --- a/src/main/java/com/example/healthylife/entity/TodayCommentsEntity.java +++ b/src/main/java/com/example/healthylife/entity/TodayCommentsEntity.java @@ -1,9 +1,7 @@ package com.example.healthylife.entity; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.ToString; +import com.fasterxml.jackson.annotation.JsonBackReference; +import lombok.*; import javax.persistence.*; import java.io.Serializable; @@ -12,6 +10,7 @@ @ToString @Entity @Getter +@Setter @Table(name = "today_comments") @NoArgsConstructor public class TodayCommentsEntity implements Serializable { @@ -32,6 +31,7 @@ public class TodayCommentsEntity implements Serializable { //게시글 넘버 시퀀스(foreign key) @ManyToOne @JoinColumn(name = "today_sq") + @JsonBackReference private TodayEntity todayEntity; //작성자 @@ -53,17 +53,4 @@ public TodayCommentsEntity(long todayCommentsSq, String todayCommentsContents, D this.user = user; } - - // - //예시 데이터 여러개 넣기(반복으로 아무거나) - //테이블 만들기 - //controller에 crud - //자유게시판 형식 - //글제목,작성자,작성날짜,글내용,댓글 - //글전체조회,키워드검색 - - - - - } diff --git a/src/main/java/com/example/healthylife/entity/TodayEntity.java b/src/main/java/com/example/healthylife/entity/TodayEntity.java index d4b0e37..7a3b5a3 100644 --- a/src/main/java/com/example/healthylife/entity/TodayEntity.java +++ b/src/main/java/com/example/healthylife/entity/TodayEntity.java @@ -1,10 +1,12 @@ package com.example.healthylife.entity; +import com.fasterxml.jackson.annotation.JsonManagedReference; import lombok.*; import javax.persistence.*; import java.io.Serializable; import java.util.Date; +import java.util.List; @ToString @Entity @@ -14,11 +16,6 @@ @NoArgsConstructor public class TodayEntity implements Serializable { - //회원 아이디(유저 참조) - //오운완 시퀀스 - //오운완 글제목 - //오운완 글내용 - //오운완 작성날짜 @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -47,16 +44,25 @@ public class TodayEntity implements Serializable { @JoinColumn(name = "user_sq") private UserEntity user; + //댓글 + @JsonManagedReference + @OneToMany(mappedBy = "todayEntity", cascade = CascadeType.ALL, orphanRemoval = true) + private List comments; + + @Column(name = "image_url") + private String imageurl; + // builder @Builder(toBuilder = true) public TodayEntity(long todaySq, String todayContents, long todayHearts, Date todayCreated, - UserEntity user){ + UserEntity user, String imageurl){ this.todaySq = todaySq; this.todayContents = todayContents; this.todayHearts = todayHearts; this.todayCreated = todayCreated; this.user = user; + this.imageurl = imageurl; } diff --git a/src/main/java/com/example/healthylife/repository/HeartRepository.java b/src/main/java/com/example/healthylife/repository/HeartRepository.java new file mode 100644 index 0000000..1e839aa --- /dev/null +++ b/src/main/java/com/example/healthylife/repository/HeartRepository.java @@ -0,0 +1,18 @@ +package com.example.healthylife.repository; + + +import com.example.healthylife.entity.HeartEntity; +import com.example.healthylife.entity.TodayEntity; +import com.example.healthylife.entity.UserEntity; +import org.apache.ibatis.annotations.Param; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.Optional; + +public interface HeartRepository extends JpaRepository { + Optional findByUserAndToday(UserEntity user, TodayEntity todayentity); + + @Query("SELECT COUNT(h) FROM HeartEntity h WHERE h.today = :today AND h.status = true") + Long HeartCount(@Param("today") TodayEntity today); +} diff --git a/src/main/java/com/example/healthylife/repository/TodayCommentsRepository.java b/src/main/java/com/example/healthylife/repository/TodayCommentsRepository.java index 407003e..fae2459 100644 --- a/src/main/java/com/example/healthylife/repository/TodayCommentsRepository.java +++ b/src/main/java/com/example/healthylife/repository/TodayCommentsRepository.java @@ -13,4 +13,6 @@ public interface TodayCommentsRepository extends JpaRepository findByUserUserId(String userId); + + List findByTodayEntity_todaySq(long todaySq); } diff --git a/src/main/java/com/example/healthylife/repository/TodayRepository.java b/src/main/java/com/example/healthylife/repository/TodayRepository.java index dfcb136..b7e4355 100644 --- a/src/main/java/com/example/healthylife/repository/TodayRepository.java +++ b/src/main/java/com/example/healthylife/repository/TodayRepository.java @@ -5,11 +5,15 @@ import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Optional; @Repository public interface TodayRepository extends JpaRepository { -// 오운완 내가 작성한 글 조회 + // 오운완 내가 작성한 글 조회 List findByUserUserId(String userId); - //jpa 규칙 : findBy 라는 이름으로 시작해야 jpa 가 올바르게 쿼리를 생성 - // 규칙에 맞지 않으면 메소드 생성 X 오류남 + + //today 시퀀스 단일 조화 + Optional findByTodaySq(long todaySq); + + Optional findByTodaySq(Long todaySq); } diff --git a/src/main/java/com/example/healthylife/service/HeartService.java b/src/main/java/com/example/healthylife/service/HeartService.java new file mode 100644 index 0000000..6553f3c --- /dev/null +++ b/src/main/java/com/example/healthylife/service/HeartService.java @@ -0,0 +1,40 @@ +package com.example.healthylife.service; + +import com.example.healthylife.entity.CommunityEntity; +import com.example.healthylife.entity.HeartEntity; +import com.example.healthylife.entity.TodayEntity; +import com.example.healthylife.entity.UserEntity; +import com.example.healthylife.repository.HeartRepository; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.Optional; + +@Service +public class HeartService { + @Autowired + private HeartRepository heartRepository; + + public void Like(UserEntity user, TodayEntity today) { + Optional existingHeart = heartRepository.findByUserAndToday(user, today); + + if (existingHeart.isPresent()) { + HeartEntity heart = existingHeart.get(); + heart.toggleStatus(); + heartRepository.save(heart); + } else { + HeartEntity heart = HeartEntity.builder() + .user(user) + .today(today) + .status(true) + .createdAt(LocalDateTime.now()) + .build(); + heartRepository.save(heart); + } + } + + public Long HeartCount(TodayEntity today) { + return heartRepository.HeartCount(today); + } +} diff --git a/src/main/java/com/example/healthylife/service/S3Service.java b/src/main/java/com/example/healthylife/service/S3Service.java new file mode 100644 index 0000000..35a9042 --- /dev/null +++ b/src/main/java/com/example/healthylife/service/S3Service.java @@ -0,0 +1,45 @@ +package com.example.healthylife.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import software.amazon.awssdk.core.sync.RequestBody; +import software.amazon.awssdk.services.s3.S3Client; +import software.amazon.awssdk.services.s3.model.PutObjectRequest; +import software.amazon.awssdk.services.s3.model.PutObjectResponse; + +import java.io.IOException; + +@Service +public class S3Service { + + private final S3Client s3Client; + + @Autowired + public S3Service(S3Client s3Client) { + this.s3Client = s3Client; + } + + private final String bucketName = "today-image1"; + + public String uploadFileToS3(MultipartFile file) { + String fileName = file.getOriginalFilename(); + + try { + PutObjectRequest putObjectRequest = PutObjectRequest.builder() + .bucket(bucketName) + .key(fileName) + .build(); + + PutObjectResponse putObjectResponse = s3Client.putObject(putObjectRequest, + RequestBody.fromInputStream(file.getInputStream(), file.getSize())); + + // 반환된 URL은 직접 생성해야 합니다. + return "https://" + bucketName + ".s3.amazonaws.com/" + fileName; + + } catch (IOException e) { + throw new RuntimeException("S3 파일 업로드 실패", e); + } + } + +} diff --git a/src/main/java/com/example/healthylife/service/TodayService.java b/src/main/java/com/example/healthylife/service/TodayService.java index 0368997..33f5f9e 100644 --- a/src/main/java/com/example/healthylife/service/TodayService.java +++ b/src/main/java/com/example/healthylife/service/TodayService.java @@ -1,20 +1,31 @@ package com.example.healthylife.service; -import com.example.healthylife.entity.TodayEntity; +import com.example.healthylife.entity.*; +import com.example.healthylife.repository.TodayCommentsRepository; import com.example.healthylife.repository.TodayRepository; import com.example.healthylife.repository.UserRepository; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; +import javax.transaction.Transactional; +import java.util.Date; import java.util.List; +import java.util.Optional; + @RequiredArgsConstructor @Service public class TodayService { private final TodayRepository todayRepository; private final UserRepository userRepository; + private final TodayCommentsRepository todayCommentsRepository; + @Autowired + private final S3Service s3Service; + //전체리스트 public List todayList() { return todayRepository.findAll(); } @@ -23,23 +34,60 @@ public List findMyTodayContents(String userId) { return todayRepository.findByUserUserId(userId); } - //오운완 내가 쓴 글 찾기 -// @Override -// public List findMyToday(String userId) { -// return todayRepository.findByUserToday(userId); -// } + public Optional findbytodaysq(long todaysq) { return todayRepository.findByTodaySq(todaysq);} public TodayEntity registerToday(TodayEntity todayEntity) { return todayRepository.save(todayEntity); } - public TodayEntity updateEntity(TodayEntity todayEntity) { - return todayRepository.save(todayEntity); + @Transactional + public TodayEntity updateEntity(Long todayId, TodayEntity updatedTodayEntity, String username) { + UserEntity user = userRepository.findByUserId(username) + .orElseThrow(() -> new RuntimeException("유저가 없습니다.")); + TodayEntity existingTodayEntity = todayRepository.findById(todayId) + .orElseThrow(() -> new RuntimeException("오운완 글이 없습니다.")); + // 권한 체크: 현재 로그인한 사용자가 작성자와 동일한지 확인 + if (!existingTodayEntity.getUser().getUserId().equals(username)) { + throw new SecurityException("작성자만 수정할 수 있습니다."); + } + // 오운완 글의 내용 업데이트 + if (updatedTodayEntity.getTodayContents() != null) { + existingTodayEntity.setTodayContents(updatedTodayEntity.getTodayContents()); + } + if (updatedTodayEntity.getTodayContents() != null) { + existingTodayEntity.setTodayContents(updatedTodayEntity.getTodayContents()); + } + // 필요한 필드만 업데이트 추가 가능 + + return todayRepository.save(existingTodayEntity); } public void deleteByTodaySq(long todaySq) { todayRepository.deleteById(todaySq); } + //댓글 단일 조회 + public Optional findTodayBySq(Long todaysq) { + Optional today = todayRepository.findByTodaySq(todaysq); + today.ifPresent(c -> { + List comments = todayCommentsRepository.findByTodayEntity_todaySq(todaysq); + c.setComments(comments); + }); + return today; + } + + public TodayEntity createTodayPost(String content, MultipartFile file, UserEntity user) { + String imageUrl = s3Service.uploadFileToS3(file); + + TodayEntity todayEntity = TodayEntity.builder() + .todayContents(content) + .todayHearts(0) + .todayCreated(new Date()) + .user(user) + .imageurl(imageUrl) + .build(); + + return todayRepository.save(todayEntity); + } }