Skip to content

Commit 8236ce6

Browse files
authored
Merge pull request #30 from umc-timeto/develop
[Feat] 폴더 도메인 추가, 배포 설정 수정
2 parents 962019d + ac2d482 commit 8236ce6

File tree

13 files changed

+318
-1
lines changed

13 files changed

+318
-1
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ repositories {
2525
}
2626

2727
dependencies {
28-
// Lombok
28+
// Lombok
2929
compileOnly 'org.projectlombok:lombok'
3030
annotationProcessor 'org.projectlombok:lombok'
3131

docker-compose-dev.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ services:
4646
KAKAO_CLIENT_SECRET: ${KAKAO_CLIENT_SECRET}
4747
KAKAO_REDIRECT_URI: ${KAKAO_REDIRECT_URI}
4848

49+
JWT_ACCESS_EXP_SECONDS: ${JWT_ACCESS_EXP_SECONDS}
50+
JWT_REFRESH_EXP_SECONDS: ${JWT_REFRESH_EXP_SECONDS}
51+
52+
4953
networks:
5054
- timetoDevNetwork
5155

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.umc.timeto.folder.controller;
2+
3+
import com.umc.timeto.folder.dto.*;
4+
import com.umc.timeto.folder.service.FolderService;
5+
import com.umc.timeto.global.apiPayload.code.ResponseCode;
6+
import com.umc.timeto.global.apiPayload.dto.ResponseDTO;
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import jakarta.validation.Valid;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.http.ResponseEntity;
11+
import org.springframework.security.core.Authentication;
12+
import org.springframework.security.core.context.SecurityContextHolder;
13+
import org.springframework.web.bind.annotation.*;
14+
15+
@RestController
16+
@RequiredArgsConstructor
17+
@RequestMapping("/api")
18+
public class FolderController {
19+
20+
private final FolderService folderService;
21+
22+
private Long getMemberId(Authentication authentication) {
23+
return (Long) authentication.getPrincipal();
24+
}
25+
26+
@Operation(summary = "목표별 폴더 리스트", description = "인증된 사용자의 목표별 폴더를 조회합니다")
27+
@GetMapping("/goal/folder/list")
28+
public ResponseEntity<?> getFolderList(@RequestParam Long goalId, Authentication authentication) {
29+
30+
Long memberId = getMemberId(authentication);
31+
var res = folderService.getFolderList(goalId, memberId);
32+
33+
return ResponseEntity
34+
.status(ResponseCode.SUCCESS_GET_FOLDERLIST.getStatus().value())
35+
.body(new ResponseDTO<>(ResponseCode.SUCCESS_GET_FOLDERLIST, res));
36+
}
37+
38+
@Operation(summary = "폴더 추가", description = "인증된 사용자가 새로운 폴더를 생성합니다")
39+
@PostMapping("/folder")
40+
public ResponseEntity<?> addFolder(
41+
@RequestParam Long goalId,
42+
@Valid @RequestBody FolderAddDTO dto,
43+
Authentication authentication
44+
) {
45+
Long memberId = getMemberId(authentication);
46+
FolderResponseDTO res = folderService.addFolder(goalId, dto, memberId);
47+
48+
return ResponseEntity
49+
.status(ResponseCode.SUCCESS_ADD_FOLDER.getStatus().value())
50+
.body(new ResponseDTO<>(ResponseCode.SUCCESS_ADD_FOLDER, res));
51+
}
52+
53+
@Operation(summary = "폴더 수정", description = "인증된 사용자가 폴더를 수정합니다")
54+
@PatchMapping("/folder/{folderId}")
55+
public ResponseEntity<?> updateFolder(
56+
@PathVariable Long folderId,
57+
@Valid @RequestBody FolderUpdateDTO dto,
58+
Authentication authentication
59+
) {
60+
Long memberId = getMemberId(authentication);
61+
FolderResponseDTO res = folderService.updateFolder(folderId, dto, memberId);
62+
63+
return ResponseEntity
64+
.status(ResponseCode.SUCCESS_UPDATE_FOLDER.getStatus().value())
65+
.body(new ResponseDTO<>(ResponseCode.SUCCESS_UPDATE_FOLDER, res));
66+
}
67+
68+
@Operation(summary = "폴더 삭제", description = "인증된 사용자가 폴더를 삭제합니다")
69+
@DeleteMapping("/folder/{folderId}")
70+
public ResponseEntity<?> deleteFolder(@PathVariable Long folderId, Authentication authentication) {
71+
72+
Long memberId = getMemberId(authentication);
73+
folderService.deleteFolder(folderId, memberId);
74+
75+
return ResponseEntity
76+
.status(ResponseCode.SUCCESS_DELETE_FOLDER.getStatus().value())
77+
.body(new ResponseDTO<>(ResponseCode.SUCCESS_DELETE_FOLDER, null));
78+
}
79+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.umc.timeto.folder.dto;
2+
3+
4+
import com.umc.timeto.folder.entity.Folder;
5+
import com.umc.timeto.goal.entity.Goal;
6+
import jakarta.validation.constraints.NotBlank;
7+
import lombok.AllArgsConstructor;
8+
import lombok.Builder;
9+
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
11+
12+
13+
@Getter
14+
@Builder
15+
@NoArgsConstructor
16+
@AllArgsConstructor
17+
public class FolderAddDTO {
18+
19+
@NotBlank(message = "folderName은 필수 입력 값입니다.")
20+
private String folderName;
21+
22+
public Folder toEntity(Goal goal) {
23+
return Folder.builder()
24+
.name(folderName)
25+
.goal(goal)
26+
.build();
27+
}
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.umc.timeto.folder.dto;
2+
3+
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
@Getter
10+
@Builder
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
public class FolderResponseDTO {
14+
15+
private Long id;
16+
private String name;
17+
18+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package com.umc.timeto.folder.dto;
2+
3+
import jakarta.validation.constraints.NotBlank;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Builder;
6+
import lombok.Getter;
7+
import lombok.NoArgsConstructor;
8+
9+
@Getter
10+
@Builder
11+
@NoArgsConstructor
12+
@AllArgsConstructor
13+
public class FolderUpdateDTO {
14+
15+
@NotBlank(message = "folderName은 필수 입력 값입니다.")
16+
private String folderName;
17+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.umc.timeto.folder.entity;
2+
3+
import com.umc.timeto.goal.entity.Goal;
4+
import jakarta.persistence.*;
5+
import lombok.*;
6+
7+
@Entity
8+
@Getter
9+
@Builder
10+
@AllArgsConstructor
11+
@NoArgsConstructor
12+
public class Folder {
13+
14+
@Id
15+
@GeneratedValue(strategy = GenerationType.IDENTITY)
16+
private Long folderId;
17+
18+
@Column(nullable = false)
19+
private String name;
20+
21+
@ManyToOne(fetch = FetchType.LAZY)
22+
@JoinColumn(name = "goal_id", nullable = false)
23+
private Goal goal;
24+
25+
public Folder(String name, Goal goal) {
26+
this.name = name;
27+
this.goal = goal;
28+
}
29+
30+
public void updateName(String name) {
31+
this.name = name;
32+
}
33+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.umc.timeto.folder.repository;
2+
3+
import com.umc.timeto.folder.entity.Folder;
4+
import com.umc.timeto.goal.entity.Goal;
5+
import org.springframework.data.jpa.repository.JpaRepository;
6+
import org.springframework.stereotype.Repository;
7+
8+
import java.util.List;
9+
10+
@Repository
11+
public interface FolderRepository extends JpaRepository<Folder, Long> {
12+
13+
List<Folder> findAllByGoal(Goal goal);
14+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.umc.timeto.folder.service;
2+
3+
import com.umc.timeto.folder.dto.FolderAddDTO;
4+
import com.umc.timeto.folder.dto.FolderResponseDTO;
5+
import com.umc.timeto.folder.dto.FolderUpdateDTO;
6+
7+
import java.util.List;
8+
9+
public interface FolderService {
10+
11+
FolderResponseDTO addFolder(Long goalId, FolderAddDTO dto, Long memberId);
12+
13+
List<FolderResponseDTO> getFolderList(Long goalId, Long memberId);
14+
15+
FolderResponseDTO updateFolder(Long folderId, FolderUpdateDTO dto, Long memberId);
16+
17+
void deleteFolder(Long folderId, Long memberId);
18+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package com.umc.timeto.folder.service;
2+
3+
import com.umc.timeto.folder.dto.FolderAddDTO;
4+
import com.umc.timeto.folder.dto.FolderResponseDTO;
5+
import com.umc.timeto.folder.dto.FolderUpdateDTO;
6+
import com.umc.timeto.folder.entity.Folder;
7+
import com.umc.timeto.folder.repository.FolderRepository;
8+
import com.umc.timeto.global.apiPayload.code.ErrorCode;
9+
import com.umc.timeto.global.apiPayload.exception.GlobalException;
10+
import com.umc.timeto.goal.entity.Goal;
11+
import com.umc.timeto.goal.repository.GoalRepository;
12+
import lombok.RequiredArgsConstructor;
13+
import org.springframework.stereotype.Service;
14+
import org.springframework.transaction.annotation.Transactional;
15+
16+
import java.util.List;
17+
18+
@Service
19+
@Transactional
20+
21+
@RequiredArgsConstructor
22+
public class FolderServiceImpl implements FolderService {
23+
24+
private final FolderRepository folderRepository;
25+
private final GoalRepository goalRepository;
26+
27+
@Override
28+
public FolderResponseDTO addFolder(Long goalId, FolderAddDTO dto, Long memberId) {
29+
30+
Goal goal = goalRepository.findById(goalId)
31+
.orElseThrow(() -> new GlobalException(ErrorCode.GOAL_NOT_FOUND));
32+
33+
if (!goal.getMember().getMemberId().equals(memberId)){
34+
throw new GlobalException(ErrorCode.GOAL_FORBIDDEN);
35+
}
36+
37+
Folder folder = folderRepository.save(dto.toEntity(goal));
38+
39+
return FolderResponseDTO.builder()
40+
.id(folder.getFolderId())
41+
.name(folder.getName())
42+
.build();
43+
}
44+
45+
@Override
46+
@Transactional(readOnly = true)
47+
public List<FolderResponseDTO> getFolderList(Long goalId, Long memberId) {
48+
49+
Goal goal = goalRepository.findById(goalId)
50+
.orElseThrow(() -> new GlobalException(ErrorCode.GOAL_NOT_FOUND));
51+
52+
if (!goal.getMember().getMemberId().equals(memberId)) {
53+
throw new GlobalException(ErrorCode.GOAL_FORBIDDEN);
54+
}
55+
56+
return folderRepository.findAllByGoal(goal)
57+
.stream()
58+
.map(folder -> FolderResponseDTO.builder()
59+
.id(folder.getFolderId())
60+
.name(folder.getName())
61+
.build())
62+
.toList();
63+
}
64+
65+
@Override
66+
public FolderResponseDTO updateFolder(Long folderId, FolderUpdateDTO dto, Long memberId) {
67+
68+
Folder folder = folderRepository.findById(folderId)
69+
.orElseThrow(() -> new GlobalException(ErrorCode.FOLDER_NOT_FOUND));
70+
71+
if (!folder.getGoal().getMember().getMemberId().equals(memberId)) {
72+
throw new GlobalException(ErrorCode.GOAL_FORBIDDEN);
73+
}
74+
75+
folder.updateName(dto.getFolderName());
76+
77+
return FolderResponseDTO.builder()
78+
.id(folder.getFolderId())
79+
.name(folder.getName())
80+
.build();
81+
}
82+
83+
@Override
84+
public void deleteFolder(Long folderId, Long memberId) {
85+
86+
Folder folder = folderRepository.findById(folderId)
87+
.orElseThrow(() -> new GlobalException(ErrorCode.FOLDER_NOT_FOUND));
88+
89+
if (!folder.getGoal().getMember().getMemberId().equals(memberId)) {
90+
throw new GlobalException(ErrorCode.GOAL_FORBIDDEN);
91+
}
92+
93+
folderRepository.delete(folder);
94+
}
95+
}

0 commit comments

Comments
 (0)