Skip to content

Commit

Permalink
Merge pull request #94 from Team-B1ND/Fix/#93
Browse files Browse the repository at this point in the history
fix: Bus 엔티티 비관적 락 적용, refactor: Bus UseCase, Service 분리
  • Loading branch information
suw0n committed Jul 16, 2024
2 parents 08c1d33 + 757c72f commit 19c3558
Show file tree
Hide file tree
Showing 12 changed files with 342 additions and 236 deletions.
91 changes: 0 additions & 91 deletions dodam-api/src/main/java/b1nd/dodamapi/bus/BusController.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package b1nd.dodamapi.bus.handler;

import b1nd.dodamapi.bus.usecase.BusApplicationUseCase;
import b1nd.dodamapi.common.response.Response;
import b1nd.dodamapi.common.response.ResponseData;
import b1nd.dodamapi.bus.usecase.BusUseCase;
import b1nd.dodamapi.bus.usecase.dto.req.BusReq;
import b1nd.dodamapi.bus.usecase.dto.res.BusRes;
import b1nd.dodamcore.bus.domain.entity.Bus;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/bus")
@RequiredArgsConstructor
public class BusController {

private final BusUseCase busUseCase;
private final BusApplicationUseCase busApplicationUseCase;

@PostMapping
public Response registerBus(@RequestBody @Valid BusReq req) {
return busUseCase.register(req);
}

@PatchMapping("/{id}")
public Response modifyBus(@PathVariable int id, @RequestBody BusReq req) {
return busUseCase.modify(id, req);
}

@DeleteMapping("/{id}")
public Response deleteBus(@PathVariable int id) {
return busUseCase.delete(id);
}

@GetMapping
public ResponseData<List<Bus>> getValidBuses() {
return busUseCase.getValid();
}

@GetMapping("/list")
public ResponseData<List<BusRes>> getBuses(@RequestParam int page, @RequestParam int limit) {
return busUseCase.getBuses(page, limit);
}

@GetMapping("/date")
public ResponseData<List<BusRes>> getBusesByDate(
@RequestParam int year,
@RequestParam int month,
@RequestParam int day
) {
return busUseCase.getBusesByDate(year, month, day);
}

@GetMapping("/apply")
public ResponseData<Bus> getMy() {
return busUseCase.getMy();
}

@PostMapping("/apply/{id}")
public Response applyBus(@PathVariable int id) {
return busApplicationUseCase.apply(id);
}

@PatchMapping("/apply/{id}")
public Response modifyApplication(@PathVariable int id) {
return busApplicationUseCase.modify(id);
}

@DeleteMapping("/apply/{id}")
public Response cancelApplication(@PathVariable int id) {
return busApplicationUseCase.cancel(id);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package b1nd.dodamapi.bus.usecase;

import b1nd.dodamapi.common.response.Response;
import b1nd.dodamcore.bus.application.BusApplicationService;
import b1nd.dodamcore.bus.application.BusService;
import b1nd.dodamcore.bus.domain.entity.Bus;
import b1nd.dodamcore.bus.domain.entity.BusMember;
import b1nd.dodamcore.bus.domain.exception.BusAlreadyApplyException;
import b1nd.dodamcore.bus.domain.exception.BusMemberNotFoundException;
import b1nd.dodamcore.common.util.ZonedDateTimeUtil;
import b1nd.dodamcore.member.application.MemberService;
import b1nd.dodamcore.member.domain.entity.Student;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.function.Consumer;


@Component
@Transactional(rollbackFor = Exception.class)
@RequiredArgsConstructor
public class BusApplicationUseCase {

private final BusService busService;
private final BusApplicationService busApplicationService;
private final MemberService memberService;

public Response apply(int busId) {
Bus bus = busService.getByIdForUpdate(busId);
Student student = memberService.getStudentFromSession();
if(busApplicationService.hasValidApplication(student, ZonedDateTimeUtil.nowToLocalDateTime())) {
throw new BusAlreadyApplyException();
}
busApplicationService.save(
BusMember.builder()
.bus(bus)
.student(student)
.build()
);
return Response.created("버스 신청 성공");
}

public Response modify(int busId) {
Bus newBus = busService.getByIdForUpdate(busId);
Student student = memberService.getStudentFromSession();
findValidApplication(
student,
ZonedDateTimeUtil.nowToLocalDateTime(),
(busMember) -> {
Bus oldBus = busService.getByIdForUpdate(busMember.getBus().getId());
oldBus.decreaseApplyCount();
busMember.modifyBus(newBus);
newBus.increaseApplyCount();
}
);
return Response.noContent("버스 신청 수정 성공");
}

public Response cancel(int busId) {
Student student = memberService.getStudentFromSession();
findValidApplication(
student,
ZonedDateTimeUtil.nowToLocalDateTime(),
(busMember) -> {
Bus bus = busService.getByIdForUpdate(busId);
bus.decreaseApplyCount();
busApplicationService.delete(busMember);
}
);
return Response.noContent("버스 신청 취소 성공");
}

private void findValidApplication(Student student, LocalDateTime now, Consumer<? super BusMember> action) {
busApplicationService.findValidApplication(student, now)
.ifPresentOrElse(
action,
() -> {
throw new BusMemberNotFoundException();
}
);
}

}
91 changes: 91 additions & 0 deletions dodam-api/src/main/java/b1nd/dodamapi/bus/usecase/BusUseCase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package b1nd.dodamapi.bus.usecase;

import b1nd.dodamapi.bus.usecase.dto.req.BusReq;
import b1nd.dodamapi.bus.usecase.dto.res.BusMemberRes;
import b1nd.dodamapi.bus.usecase.dto.res.BusRes;
import b1nd.dodamapi.common.response.Response;
import b1nd.dodamapi.common.response.ResponseData;
import b1nd.dodamcore.bus.application.BusApplicationService;
import b1nd.dodamcore.bus.application.BusService;
import b1nd.dodamcore.bus.domain.entity.Bus;
import b1nd.dodamcore.bus.domain.entity.BusMember;
import b1nd.dodamcore.common.util.ModifyUtil;
import b1nd.dodamcore.common.util.ZonedDateTimeUtil;
import b1nd.dodamcore.member.application.MemberService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class BusUseCase {

private final BusService busService;
private final BusApplicationService busApplicationService;
private final MemberService memberService;

@Transactional(rollbackFor = Exception.class)
public Response register(BusReq req) {
busService.save(req.mapToBus());
return Response.created("버스 등록 성공");
}

@Transactional(rollbackFor = Exception.class)
public Response modify(int id, BusReq modifyBusReq) {
Bus bus = busService.getByIdForUpdate(id);
bus.updateBus(
ModifyUtil.modifyIfNotNull(modifyBusReq.busName(), bus.getBusName()),
ModifyUtil.modifyIfNotNull(modifyBusReq.description(), bus.getDescription()),
ModifyUtil.modifyIfNotNull(modifyBusReq.leaveTime(), bus.getLeaveTime()),
ModifyUtil.modifyIfNotNull(modifyBusReq.timeRequired(), bus.getTimeRequired()),
ModifyUtil.modifyIfNotZero(modifyBusReq.peopleLimit(), bus.getPeopleLimit())
);
return Response.noContent("버스 수정 성공");
}

@Transactional(rollbackFor = Exception.class)
public Response delete(int id) {
Bus bus = busService.getById(id);
busService.delete(bus);
return Response.noContent("버스 삭제 성공");
}

public ResponseData<List<Bus>> getValid() {
LocalDateTime now = ZonedDateTimeUtil.nowToLocalDateTime();
return ResponseData.ok("유효 버스 조회 성공", busService.getValid(now, now.plusDays(7)));
}

public ResponseData<List<BusRes>> getBuses(int page, int limit) {
return ResponseData.ok("버스 조회 성공", parseToBusList(busService.getAll(page, limit)));
}

public ResponseData<List<BusRes>> getBusesByDate(int year, int month, int day) {
return ResponseData.ok("해당 날짜의 버스 조회 성공", parseToBusList(busService.getAllByDate(LocalDate.of(year, month, day))));
}

private List<BusRes> parseToBusList(List<Bus> buses) {
return buses.stream()
.map(bus -> BusRes.createFromBus(
bus,
getBusApplications(bus).stream()
.map(BusMemberRes::createFromBusMember)
.toList()
)
).toList();
}

private List<BusMember> getBusApplications(Bus bus) {
return busApplicationService.getByBus(bus);
}

public ResponseData<Bus> getMy() {
int studentId = memberService.getStudentFromSession().getId();
return ResponseData.ok("신청한 버스 조회 성공", busService.getByStudent(ZonedDateTimeUtil.nowToLocalDateTime(), studentId));
}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package b1nd.dodamcore.bus.application.dto.req;
package b1nd.dodamapi.bus.usecase.dto.req;

import b1nd.dodamcore.bus.domain.entity.Bus;
import jakarta.validation.constraints.NotEmpty;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package b1nd.dodamcore.bus.application.dto.res;
package b1nd.dodamapi.bus.usecase.dto.res;

import b1nd.dodamcore.bus.domain.entity.BusMember;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package b1nd.dodamcore.bus.application.dto.res;
package b1nd.dodamapi.bus.usecase.dto.res;

import b1nd.dodamcore.bus.domain.entity.Bus;

Expand Down
Loading

0 comments on commit 19c3558

Please sign in to comment.