Skip to content

Commit

Permalink
sprint 2 (#14)
Browse files Browse the repository at this point in the history
* refactor: add field to response

* feat: add api to seeing workout confirmation list

* feat: add api to seeing workout confirmation detail

* feat: add api to tackling to workout confirmation

* feat: add api of voting to tackle

* fix: add exception handler

* fix: add @validated

* refactor: rename diction

* feat: add ddl.sql

* feat: add api of seeing objection

* fix: calculate vote result when objection is closed

* refactor: add field to response dto
  • Loading branch information
aiaiaiai1 authored Nov 17, 2024
1 parent 441b114 commit 4b2a464
Show file tree
Hide file tree
Showing 33 changed files with 1,143 additions and 260 deletions.
4 changes: 2 additions & 2 deletions src/main/java/gymmi/controller/S3Controller.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public ResponseEntity<PresignedUrlResponse> s3(

@GetMapping("/images/workout-proof/presignedUrl/get")
public ResponseEntity<String> s3(
@RequestParam("imageUrl") String filenmae
@RequestParam("filename") String filename
) {
String presignedUrl = s3Service.getPresignedUrl(filenmae);
String presignedUrl = s3Service.getPresignedUrl(filename);
return ResponseEntity.ok().body(presignedUrl);
}

Expand Down
74 changes: 14 additions & 60 deletions src/main/java/gymmi/exceptionhandler/ExceptionController.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gymmi.exceptionhandler.exception.GymmiException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
Expand All @@ -12,74 +13,27 @@
public class ExceptionController {

@ExceptionHandler
public ResponseEntity<ErrorResponse> handleAll(GymmiException e, HttpServletRequest request) {
public ResponseEntity<ErrorResponse> handleAllCustom(GymmiException e, HttpServletRequest request) {
ErrorResponse response = new ErrorResponse(e.getExceptionCode().name(), e.getMessage());
log(e, request.getRequestURI());
return ResponseEntity.status(e.getStatusCode()).body(response);
}

// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle400Exception(BadRequestException e, HttpServletRequest request) {
// ErrorResponse response = new ErrorResponse(e.getErrorTitle(), e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.badRequest().body(response);
// }
//
// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle400Exception(ConstraintViolationException e, HttpServletRequest request) {
// ErrorResponse response = new ErrorResponse("INVALID_INPUT_VALUE", e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.badRequest().body(response);
// }
//
// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle401Exception(AuthenticationException e, HttpServletRequest request) {
// ErrorResponse response = new ErrorResponse(e.getErrorTitle(), e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(response);
// }
//
// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle403Exception(NotHavePermissionException e, HttpServletRequest request) {
// ErrorResponse response = new ErrorResponse(e.getErrorTitle(), e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.status(HttpStatus.FORBIDDEN).body(response);
// }
//
// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle404Exception(NotFoundException e, HttpServletRequest request) {
// ErrorResponse response = new ErrorResponse(e.getErrorTitle(), e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.status(HttpStatus.NOT_FOUND).body(response);
// }
//
// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle500Exception(GymmiException e, HttpServletRequest request) {
// ErrorResponse response = new ErrorResponse(e.getMessage(), e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
// }

// @ExceptionHandler
// public ResponseEntity<ErrorResponse> handle500Exception(
// Exception e,
// HttpServletRequest request,
// HttpServletResponse resp
// ) {
// ErrorResponse response = new ErrorResponse("ERROR", e.getMessage());
// log(e, request.getRequestURI());
// return ResponseEntity.status(500).body(response);
// }
@ExceptionHandler
public ResponseEntity<ErrorResponse> handleAll(Exception e, HttpServletRequest request) {
ErrorResponse response = new ErrorResponse("백엔드에게 문의하세요", e.getMessage());
log(e, request.getRequestURI());
return ResponseEntity.internalServerError().body(response);
}

private void log(Exception e, String requestURI) {
log.warn(System.lineSeparator() +
"[에러 발생 로그]" + System.lineSeparator() +
"request-url : {}" + System.lineSeparator() +
"error: {}",
requestURI, e.getMessage(), e);
@ExceptionHandler
public ResponseEntity<ErrorResponse> handle400Exception(ConstraintViolationException e, HttpServletRequest request) {
ErrorResponse response = new ErrorResponse("INVALID_INPUT_VALUE", e.getMessage());
log(e, request.getRequestURI());
return ResponseEntity.badRequest().body(response);
}

private void log1(Exception e, String requestURI) {
private void log(Exception e, String requestURI) {
log.warn(System.lineSeparator() +
"[에러 발생 로그]" + System.lineSeparator() +
"request-url : {}" + System.lineSeparator() +
Expand Down
12 changes: 7 additions & 5 deletions src/main/java/gymmi/exceptionhandler/message/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@ public enum ErrorCode {
INVALID_WORKSPACE_GOAL_SCORE("목표점수는 100점에서 1000점까지 가능합니다.", 400),
INVALID_WORKSPACE_MISSION_SCORE("미션 점수는 1~10점까지 가능합니다.", 400),
NO_WORKOUT_HISTORY_EXIST_IN_WORKSPACE("해당 워크스페이스의 운동 기록이 아니에요", 403),
NO_OBJECTION_EXIST_IN_WORKSPACE("해당 워크스페이스와 관련 정보가 아니에요", 403),
ALREADY_OBJECTED("이미 이의 신청되었어요.", 400),
NOT_FOUND_OBJECTION("해당 이의 신청이 존재하지 않습니다.", 404),
ALREADY_VOTED("이미 투표하였습니다.", 400),
ALREADY_CLOSED_OBJECTION("이미 종료되었습니다", 400),


// user
Expand Down Expand Up @@ -61,10 +66,7 @@ public enum ErrorCode {
UNSUPPORTED_TYPE("지원하지 않는 type 입니다.", 400),
NOT_FOUND_WORKER("존재하지 않는 참여자 입니다.", 400),
INVALID_DUPLICATION_CHECK_TYPE_VALUE("잘못된 query param value 입니다. : " + DuplicationCheckType.class.getName(), 400),
INVALID_WORKSPACE_STATUS_VALUE("잘못된 query param value 입니다. : " + WorkspaceStatus.class.getName(), 400),


;
INVALID_WORKSPACE_STATUS_VALUE("잘못된 query param value 입니다. : " + WorkspaceStatus.class.getName(), 400);

private final String message;
private final int statusCode;
Expand All @@ -81,4 +83,4 @@ public String getMessage() {
public int getStatusCode() {
return statusCode;
}
}
}
89 changes: 59 additions & 30 deletions src/main/java/gymmi/workspace/controller/WorkspaceController.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,16 @@
import gymmi.global.Logined;
import gymmi.response.IdResponse;
import gymmi.workspace.domain.WorkspaceStatus;
import gymmi.workspace.request.CreatingWorkspaceRequest;
import gymmi.workspace.request.EditingIntroductionOfWorkspaceRequest;
import gymmi.workspace.request.JoiningWorkspaceRequest;
import gymmi.workspace.request.MatchingWorkspacePasswordRequest;
import gymmi.workspace.request.WorkingMissionInWorkspaceRequest;
import gymmi.workspace.request.WorkoutRequest;
import gymmi.workspace.response.CheckingCreationOfWorkspaceResponse;
import gymmi.workspace.response.CheckingEntranceOfWorkspaceResponse;
import gymmi.workspace.response.InsideWorkspaceResponse;
import gymmi.workspace.response.JoinedWorkspaceResponse;
import gymmi.workspace.response.MatchingWorkspacePasswordResponse;
import gymmi.workspace.response.MissionResponse;
import gymmi.workspace.response.OpeningTasksBoxResponse;
import gymmi.workspace.response.WorkingScoreResponse;
import gymmi.workspace.response.WorkoutContextResponse;
import gymmi.workspace.response.WorkoutRecordResponse;
import gymmi.workspace.response.WorkspaceIntroductionResponse;
import gymmi.workspace.response.WorkspaceResponse;
import gymmi.workspace.request.*;
import gymmi.workspace.response.*;
import gymmi.workspace.service.WorkspaceCommandService;
import gymmi.workspace.service.WorkspaceQueryService;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequiredArgsConstructor
Expand Down Expand Up @@ -143,7 +121,7 @@ public ResponseEntity<List<MissionResponse>> seeMissionsInWorkspace(
public ResponseEntity<WorkingScoreResponse> workMissionsInWorkspace(
@Logined User user,
@PathVariable Long workspaceId,
@RequestBody WorkoutRequest request
@Validated @RequestBody WorkoutRequest request
) {
Integer workingScore = workspaceCommandService.workMissionsInWorkspace(user, workspaceId, request);
return ResponseEntity.ok().body(new WorkingScoreResponse(workingScore));
Expand Down Expand Up @@ -220,13 +198,64 @@ public ResponseEntity<Void> toggleRegistrationOfFavoriteMission(
}

@GetMapping("/workspace/{workspaceId}/missions/favorite")
public ResponseEntity<List<MissionResponse>> seeFavoriteMissions(
public ResponseEntity<List<FavoriteMissionResponse>> seeFavoriteMissions(
@Logined User user,
@PathVariable Long workspaceId
) {
List<MissionResponse> responses = workspaceQueryService.getFavoriteMissions(user, workspaceId);
List<FavoriteMissionResponse> responses = workspaceQueryService.getFavoriteMissions(user, workspaceId);
return ResponseEntity.ok().body(responses);
}

@GetMapping("/workspaces/{workspaceId}/workout-confirmations")
public ResponseEntity<List<WorkoutConfirmationResponse>> seeWorkoutConfirmations(
@Logined User user,
@PathVariable Long workspaceId,
@RequestParam int pageNumber
) {
List<WorkoutConfirmationResponse> responses = workspaceQueryService.getWorkoutConfirmations(user, workspaceId, pageNumber);
return ResponseEntity.ok().body(responses);
}

@GetMapping("/workspaces/{workspaceId}/workout-confirmations/{workoutConfirmationId}")
public ResponseEntity<WorkoutConfirmationDetailResponse> seeWorkoutConfirmation(
@Logined User user,
@PathVariable Long workspaceId,
@PathVariable Long workoutConfirmationId
) {
WorkoutConfirmationDetailResponse response = workspaceQueryService.getWorkoutConfirmation(user, workspaceId, workoutConfirmationId);
return ResponseEntity.ok().body(response);
}

@PostMapping("/workspaces/{workspaceId}/workout-confirmations/{workoutConfirmationId}")
public ResponseEntity<Void> objectToWorkoutConfirmation(
@Logined User user,
@PathVariable Long workspaceId,
@PathVariable Long workoutConfirmationId,
@Validated @RequestBody ObjectionRequest request
) {
workspaceCommandService.objectToWorkoutConfirmation(user, workspaceId, workoutConfirmationId, request);
return ResponseEntity.ok().build();
}

@PostMapping("/workspaces/{workspaceId}/objections/{objectionId}")
public ResponseEntity<Void> voteToObjection(
@Logined User user,
@PathVariable Long workspaceId,
@PathVariable Long objectionId,
@Validated @RequestBody VoteRequest request
) {
workspaceCommandService.voteToObjection(user, workspaceId, objectionId, request);
return ResponseEntity.ok().build();
}

@GetMapping("/workspaces/{workspaceId}/objections/{objectionId}")
public ResponseEntity<ObjectionResponse> seeObjection(
@Logined User user,
@PathVariable Long workspaceId,
@PathVariable Long objectionId
) {
ObjectionResponse response = workspaceQueryService.getObjection(user, workspaceId, objectionId);
return ResponseEntity.ok().body(response);
}

}
57 changes: 57 additions & 0 deletions src/main/java/gymmi/workspace/domain/ObjectionManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package gymmi.workspace.domain;

import gymmi.exceptionhandler.exception.AlreadyExistException;
import gymmi.exceptionhandler.exception.InvalidStateException;
import gymmi.exceptionhandler.message.ErrorCode;
import gymmi.workspace.domain.entity.Objection;
import gymmi.workspace.domain.entity.Vote;
import gymmi.workspace.domain.entity.Worker;
import lombok.Getter;

@Getter
public class ObjectionManager {
private final Objection objection;
private boolean isApproved;

public ObjectionManager(Objection objection) {
this.objection = objection;
this.isApproved = false;
}

public Vote createVote(Worker worker, boolean isApproved) {
if (!objection.isInProgress()) {
throw new InvalidStateException(ErrorCode.ALREADY_CLOSED_OBJECTION);
}
if (objection.hasVoteBy(worker)) {
throw new AlreadyExistException(ErrorCode.ALREADY_VOTED);
}
return new Vote(worker, objection, isApproved);
}

public boolean closeIfOnMajorityOrDone(int workerCount) {
int majority = getMajority(workerCount);
if (objection.getApprovalCount() >= majority) {
objection.close();
isApproved = true;
return true;
}
if (objection.getRejectionCount() >= majority) {
objection.close();
return true;
}
if (objection.getVoteCount() >= workerCount) {
objection.close();
return true;
}
return false;
}


private int getMajority(int workerCount) {
if (workerCount % 2 == 0) {
return (workerCount / 2) + 1;
}
return (int) Math.round(workerCount / 2.0);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@

import gymmi.exceptionhandler.exception.InvalidStateException;
import gymmi.exceptionhandler.message.ErrorCode;
import gymmi.workspace.domain.entity.Mission;
import gymmi.workspace.domain.entity.Worker;
import gymmi.workspace.domain.entity.WorkoutHistory;
import gymmi.workspace.domain.entity.WorkoutProof;
import gymmi.workspace.domain.entity.WorkoutRecord;
import gymmi.workspace.domain.entity.Workspace;
import gymmi.workspace.domain.entity.*;
import gymmi.workspace.domain.entity.WorkoutConfirmation;

import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand All @@ -34,7 +31,7 @@ private Workspace validateStatus(Workspace workspace) {
return workspace;
}

public WorkoutHistory doWorkout(Worker worker, Map<Mission, Integer> workouts, WorkoutProof workoutProof) {
public WorkoutHistory doWorkout(Worker worker, Map<Mission, Integer> workouts, WorkoutConfirmation workoutProof) {
if (!worker.isJoinedIn(workspace)) {
throw new InvalidStateException(NOT_JOINED_WORKSPACE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
package gymmi.workspace.domain.entity;

import gymmi.entity.TimeEntity;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
Expand Down Expand Up @@ -35,4 +29,5 @@ public FavoriteMission(Worker worker, Mission mission) {
this.worker = worker;
this.mission = mission;
}

}
Loading

0 comments on commit 4b2a464

Please sign in to comment.