-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FEAT] 선택한 경로로 출발하기 및 이동 중 관련 기능 구현 #58
base: develop
Are you sure you want to change the base?
Changes from all commits
c7cb668
72bb349
84b115e
a962b37
b938ff2
6ad370c
0857cb3
bab288a
dec505b
c5509db
50e08a2
adca4da
bc8c41c
35bde55
2a7f8e9
6a25688
5044767
b1aaa99
6993f3a
1b8911d
4de1149
f79b5be
fe53f46
2585ae0
ee2e373
ca1c04e
9cc43be
7e82029
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.dubu.backend.plan.api; | ||
|
||
import com.dubu.backend.global.domain.SuccessResponse; | ||
import com.dubu.backend.plan.application.PlanService; | ||
import com.dubu.backend.plan.dto.request.PlanSaveRequest; | ||
import com.dubu.backend.plan.dto.response.PlanRecentResponse; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.HttpStatus; | ||
import org.springframework.web.bind.annotation.*; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/plans") | ||
public class PlanController { | ||
private final PlanService planService; | ||
|
||
@ResponseStatus(HttpStatus.CREATED) | ||
@PostMapping | ||
public void savePlan( | ||
@RequestAttribute("memberId") Long memberId, | ||
@RequestBody PlanSaveRequest planSaveRequest | ||
) { | ||
planService.savePlan(memberId, planSaveRequest); | ||
} | ||
|
||
@GetMapping("/recent") | ||
public SuccessResponse<PlanRecentResponse> getRecentPlan( | ||
@RequestAttribute("memberId") Long memberId | ||
) { | ||
PlanRecentResponse response = planService.findRecentPlan(memberId); | ||
|
||
return new SuccessResponse<>(response); | ||
} | ||
|
||
@ResponseStatus(HttpStatus.NO_CONTENT) | ||
@DeleteMapping | ||
public void deletePlan( | ||
@RequestAttribute("memberId") Long memberId, | ||
@RequestParam("planId") Long planId | ||
){ | ||
planService.removePlan(memberId, planId); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,94 @@ | ||||||||||||||||||||||||||||||||||||||||||
package com.dubu.backend.plan.application; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.member.domain.Member; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.member.domain.enums.Status; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.member.exception.MemberNotFoundException; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.member.infra.repository.MemberRepository; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.domain.Path; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.domain.Plan; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.dto.request.PlanSaveRequest; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.dto.response.PlanRecentResponse; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.exception.InvalidMemberStatusException; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.exception.PlanNotFoundException; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.infra.repository.PathRepository; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.plan.infra.repository.PlanRepository; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.todo.entity.Schedule; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.todo.entity.Todo; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.todo.exception.ScheduleNotFoundException; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.todo.repository.ScheduleRepository; | ||||||||||||||||||||||||||||||||||||||||||
import com.dubu.backend.todo.repository.TodoRepository; | ||||||||||||||||||||||||||||||||||||||||||
import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||
import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||||||||||||
import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
import java.time.LocalDate; | ||||||||||||||||||||||||||||||||||||||||||
import java.util.ArrayList; | ||||||||||||||||||||||||||||||||||||||||||
import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||
import java.util.stream.IntStream; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Service | ||||||||||||||||||||||||||||||||||||||||||
@RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||
public class PlanService { | ||||||||||||||||||||||||||||||||||||||||||
private final MemberRepository memberRepository; | ||||||||||||||||||||||||||||||||||||||||||
private final PlanRepository planRepository; | ||||||||||||||||||||||||||||||||||||||||||
private final PathRepository pathRepository; | ||||||||||||||||||||||||||||||||||||||||||
private final ScheduleRepository scheduleRepository; | ||||||||||||||||||||||||||||||||||||||||||
private final TodoRepository todoRepository; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Transactional | ||||||||||||||||||||||||||||||||||||||||||
public void savePlan(Long memberId, PlanSaveRequest planSaveRequest) { | ||||||||||||||||||||||||||||||||||||||||||
Member currentMember = memberRepository.findById(memberId) | ||||||||||||||||||||||||||||||||||||||||||
.orElseThrow(() -> new MemberNotFoundException(memberId)); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (currentMember.getStatus() != Status.STOP) { | ||||||||||||||||||||||||||||||||||||||||||
throw new InvalidMemberStatusException(currentMember.getStatus().name()); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Plan newPlan = Plan.createPlan(currentMember, planSaveRequest.totalSectionTime()); | ||||||||||||||||||||||||||||||||||||||||||
planRepository.save(newPlan); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
List<Path> paths = IntStream.range(0, planSaveRequest.paths().size()) | ||||||||||||||||||||||||||||||||||||||||||
.mapToObj(index -> Path.createPath(newPlan, planSaveRequest.paths().get(index), index)) | ||||||||||||||||||||||||||||||||||||||||||
.toList(); | ||||||||||||||||||||||||||||||||||||||||||
pathRepository.saveAll(paths); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Schedule schedule = scheduleRepository.findScheduleByMemberAndDate(currentMember, LocalDate.now()) | ||||||||||||||||||||||||||||||||||||||||||
.orElseThrow(() -> new ScheduleNotFoundException()); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
List<Todo> existingTodos = schedule.getTodos(); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
List<Todo> newTodos = new ArrayList<>(); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
for (int i = 0; i < existingTodos.size(); i++) { | ||||||||||||||||||||||||||||||||||||||||||
Todo original = existingTodos.get(i); | ||||||||||||||||||||||||||||||||||||||||||
// round-robin으로 Path 할당 | ||||||||||||||||||||||||||||||||||||||||||
Path assignedPath = paths.get(i % paths.size()); | ||||||||||||||||||||||||||||||||||||||||||
Todo clonedTodo = Todo.copyOf(original, assignedPath); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
newTodos.add(clonedTodo); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
todoRepository.saveAll(newTodos); | ||||||||||||||||||||||||||||||||||||||||||
currentMember.updateStatus(Status.MOVE); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Transactional(readOnly = true) | ||||||||||||||||||||||||||||||||||||||||||
public PlanRecentResponse findRecentPlan(Long memberId) { | ||||||||||||||||||||||||||||||||||||||||||
memberRepository.findById(memberId) | ||||||||||||||||||||||||||||||||||||||||||
.orElseThrow(() -> new MemberNotFoundException(memberId)); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
Plan recentPlan = planRepository.findTopByMemberIdOrderByCreatedAtDesc(memberId) | ||||||||||||||||||||||||||||||||||||||||||
.orElseThrow(() -> new PlanNotFoundException()); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
List<Path> paths = pathRepository.findByPlanWithTodosOrderByPathOrder(recentPlan); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
return PlanRecentResponse.of(recentPlan, paths); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
@Transactional | ||||||||||||||||||||||||||||||||||||||||||
public void removePlan(Long memberId, Long planId) { | ||||||||||||||||||||||||||||||||||||||||||
memberRepository.findById(memberId) | ||||||||||||||||||||||||||||||||||||||||||
.orElseThrow(() -> new MemberNotFoundException(memberId)); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
planRepository.deleteById(planId); | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. deleteById 로 해당 plan 의 존재 여부를 확인하지 않고 바로 삭제하는 것이 좋네요!! 배워갑니다!! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토끼친구는 존재 여부를 확인하고 삭제하라고 하는데 현원님은 어떻게 생각하시나요? |
||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+87
to
+93
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enforce plan ownership checks when deleting a plan. Below is an example code diff to ensure the plan is bound to the correct member: @Transactional
public void removePlan(Long memberId, Long planId) {
memberRepository.findById(memberId)
.orElseThrow(() -> new MemberNotFoundException(memberId));
+ Plan plan = planRepository.findById(planId)
+ .orElseThrow(() -> new PlanNotFoundException());
+ if (!plan.getMember().getId().equals(memberId)) {
+ throw new PlanNotFoundException(); // or any relevant unauthorized exception
+ }
- planRepository.deleteById(planId);
+ planRepository.delete(plan);
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
todo 를 하나씩 저장하는 것이 아닌 한 번에 저장하는 것이 좋았습니다!!