diff --git a/.gitignore b/.gitignore index f788dd14..832c79ba 100644 --- a/.gitignore +++ b/.gitignore @@ -45,4 +45,6 @@ LOGGING_ERROR_FILE_PATH_IS_UNDEFINED idorm-rolling.log ### Not Useed -src/main/**/master \ No newline at end of file +src/main/**/master + +src/main/**/util \ No newline at end of file diff --git a/src/main/java/idorm/idormServer/calendar/controller/CalendarAdminController.java b/src/main/java/idorm/idormServer/calendar/controller/CalendarAdminController.java index 58b90203..0c832d5b 100644 --- a/src/main/java/idorm/idormServer/calendar/controller/CalendarAdminController.java +++ b/src/main/java/idorm/idormServer/calendar/controller/CalendarAdminController.java @@ -1,7 +1,7 @@ package idorm.idormServer.calendar.controller; import idorm.idormServer.calendar.domain.Calendar; -import idorm.idormServer.calendar.dto.*; +import idorm.idormServer.calendar.dto.Calendar.*; import idorm.idormServer.calendar.service.CalendarService; import idorm.idormServer.common.DefaultResponseDto; import io.swagger.annotations.Api; @@ -36,6 +36,8 @@ public class CalendarAdminController { responseCode = "201", description = "CALENDAR_SAVED", content = @Content(schema = @Schema(implementation = CalendarAdminResponseDto.class))), + @ApiResponse(responseCode = "400", + description = "DATE_SET_INVALID"), @ApiResponse(responseCode = "401", description = "UNAUTHORIZED_MEMBER"), @ApiResponse(responseCode = "403", @@ -176,7 +178,7 @@ public ResponseEntity> updateLocation( description = "CALENDAR_UPDATED", content = @Content(schema = @Schema(implementation = CalendarAdminResponseDto.class))), @ApiResponse(responseCode = "400", - description = "CALENDARID_NEGATIVEORZERO_INVALID"), + description = "CALENDARID_NEGATIVEORZERO_INVALID / DATE_SET_INVALID"), @ApiResponse(responseCode = "401", description = "UNAUTHORIZED_MEMBER"), @ApiResponse(responseCode = "403", diff --git a/src/main/java/idorm/idormServer/calendar/controller/CalendarController.java b/src/main/java/idorm/idormServer/calendar/controller/CalendarController.java index 430f096e..3cfd0d2f 100644 --- a/src/main/java/idorm/idormServer/calendar/controller/CalendarController.java +++ b/src/main/java/idorm/idormServer/calendar/controller/CalendarController.java @@ -1,7 +1,8 @@ package idorm.idormServer.calendar.controller; import idorm.idormServer.calendar.domain.Calendar; -import idorm.idormServer.calendar.dto.*; +import idorm.idormServer.calendar.dto.Calendar.CalendarDefaultResponseDto; +import idorm.idormServer.calendar.dto.Calendar.CalendarFindManyRequestDto; import idorm.idormServer.calendar.service.CalendarService; import idorm.idormServer.common.DefaultResponseDto; import io.swagger.annotations.Api; @@ -31,7 +32,7 @@ public class CalendarController { private final CalendarService calendarService; @ApiOperation(value = "일정 다건 조회", notes = "- 모든 기숙사의 일정을 반환합니다. \n" + - "- 서버에서 종료된 일정은 제거 및 일정의 시작일자 순으로 정렬하여 반환합니다.") + "- 종료된 일정은 제거한 후, 최신 등록 순으로 응답합니다.") @ApiResponses(value = { @ApiResponse( responseCode = "200", @@ -54,7 +55,7 @@ public ResponseEntity> findMany( List calendars = calendarService.findManyByYearMonth(request.getYearMonth()); calendars.removeIf(calendar -> calendar.getEndDate().isBefore(LocalDateTime.now().plusHours(9).toLocalDate())); - calendars.sort(Comparator.comparing(Calendar::getStartDate)); + calendars.sort(Comparator.comparing(Calendar::getId, Comparator.reverseOrder())); List responses = calendars.stream() .map(calendar -> new CalendarDefaultResponseDto(calendar)).collect(Collectors.toList()); diff --git a/src/main/java/idorm/idormServer/calendar/controller/TeamCalendarController.java b/src/main/java/idorm/idormServer/calendar/controller/TeamCalendarController.java new file mode 100644 index 00000000..646c75ca --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/controller/TeamCalendarController.java @@ -0,0 +1,303 @@ +package idorm.idormServer.calendar.controller; + +import idorm.idormServer.auth.JwtTokenProvider; +import idorm.idormServer.calendar.domain.Team; +import idorm.idormServer.calendar.domain.TeamCalendar; +import idorm.idormServer.calendar.dto.Calendar.CalendarFindManyRequestDto; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindResponseDto; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarAbstractResponseDto; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarDefaultResponseDto; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarSaveRequestDto; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarUpdateRequestDto; +import idorm.idormServer.calendar.service.CalendarService; +import idorm.idormServer.calendar.service.TeamCalendarService; +import idorm.idormServer.calendar.service.TeamService; +import idorm.idormServer.common.DefaultResponseDto; +import idorm.idormServer.exception.CustomException; +import idorm.idormServer.member.domain.Member; +import idorm.idormServer.member.service.MemberService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import javax.validation.constraints.Positive; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import static idorm.idormServer.exception.ExceptionCode.TEAMCALENDAR_NOT_FOUND; + +@Api(tags = "팀 일정") +@Validated +@RestController +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public class TeamCalendarController { + + private final JwtTokenProvider jwtTokenProvider; + private final MemberService memberService; + private final TeamCalendarService teamCalendarService; + private final TeamService teamService; + private final CalendarService calendarService; + + @ApiOperation(value = "팀 일정 생성") + @ApiResponses(value = { + @ApiResponse( + responseCode = "201", + description = "TEAM_CALENDER_CREATED", + content = @Content(schema = @Schema(implementation = TeamCalendarDefaultResponseDto.class))), + @ApiResponse(responseCode = "400", + description = "DATE_SET_INVALID / *_FIELD_REQUIRED / *_LENGTH_INVALID / " + + "ILLEGAL_STATEMENT_EXPLODEDTEAM"), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND / TEAMMEMBER_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR") + }) + @PostMapping("/api/v1/member/team/calendar") + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity> createTeamCalender( + HttpServletRequest servletRequest, + @RequestBody @Valid TeamCalendarSaveRequestDto request + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Team team = teamService.findByMember(member); + + teamService.validateIsDeletedTeam(team); + teamCalendarService.validateTargetExistence(request.getTargets()); + calendarService.validateStartAndEndDate(request.getStartDate(), request.getEndDate()); + + List targets = request.getTargets().stream().distinct().collect(Collectors.toList()); + List targetMembers = teamCalendarService.validateTeamMemberExistence(team, targets); + + TeamCalendar teamCalendar = teamCalendarService.save(request.toEntity(team)); + + List childResponses = targetMembers.stream() + .map(m -> new TeamMemberFindResponseDto(m)).collect(Collectors.toList()); + + TeamCalendarDefaultResponseDto response = TeamCalendarDefaultResponseDto.builder() + .teamCalendar(teamCalendar) + .targets(childResponses) + .build(); + + return ResponseEntity.status(201) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_CALENDER_CREATED") + .responseMessage("팀 일정 생성 완료") + .data(response) + .build() + ); + } + + @ApiOperation(value = "팀 일정 수정") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_CALENDER_UPDATED", + content = @Content(schema = @Schema(implementation = TeamCalendarDefaultResponseDto.class))), + @ApiResponse(responseCode = "400", + description = "*_FIELD_REQUIRED / *_LENGTH_INVALID / TEAMCALENDARID_NEGATIVEORZERO_INVALID " + + "/ DATE_SET_INVALID / ILLEGAL_STATEMENT_EXPLODEDTEAM"), + @ApiResponse(responseCode = "403", + description = "FORBIDDEN_TEAMCALENDAR"), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND / TEAMMEMBER_NOT_FOUND / TEAMCALENDAR_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR") + }) + @PutMapping("/api/v1/member/team/calendar") + public ResponseEntity> updateTeamCalender( + HttpServletRequest servletRequest, + @RequestBody @Valid TeamCalendarUpdateRequestDto request + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Team team = teamService.findByMember(member); + + TeamCalendar teamCalendar = teamCalendarService.findById(request.getTeamCalendarId()); + teamCalendarService.validateTeamCalendarAuthorization(team, teamCalendar); + + teamService.validateIsDeletedTeam(team); + teamCalendarService.validateTargetExistence(request.getTargets()); + calendarService.validateStartAndEndDate(request.getStartDate(), request.getEndDate()); + + List targets = request.getTargets().stream().distinct().collect(Collectors.toList()); + List targetMembers = teamCalendarService.validateTeamMemberExistence(team, targets); + + teamCalendarService.update(teamCalendar, request, targets); + + List childResponses = targetMembers.stream() + .map(m -> new TeamMemberFindResponseDto(m)).collect(Collectors.toList()); + + TeamCalendarDefaultResponseDto response = TeamCalendarDefaultResponseDto.builder() + .teamCalendar(teamCalendar) + .targets(childResponses) + .build(); + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_CALENDER_UPDATED") + .responseMessage("팀 일정 수정 완료") + .data(response) + .build() + ); + } + + @ApiOperation(value = "팀 일정 삭제") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_CALENDER_DELETED", + content = @Content(schema = @Schema(implementation = Object.class))), + @ApiResponse(responseCode = "400", + description = "TEAMCALENDARID_NEGATIVEORZERO_INVALID / ILLEGAL_STATEMENT_EXPLODEDTEAM"), + @ApiResponse(responseCode = "403", + description = "FORBIDDEN_TEAMCALENDAR"), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND / TEAMCALENDAR_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR"), + }) + @DeleteMapping("/api/v1/member/team/calendar") + public ResponseEntity> deleteTeamCalender( + HttpServletRequest servletRequest, + @RequestParam(value = "teamCalendarId") + @Positive(message = "삭제할 팀일정 식별자는 양수만 가능합니다.") + Long teamCalendarId + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Team team = teamService.findByMember(member); + + TeamCalendar teamCalendar = teamCalendarService.findById(teamCalendarId); + teamCalendarService.validateTeamCalendarAuthorization(team, teamCalendar); + teamService.validateIsDeletedTeam(team); + + teamCalendarService.delete(teamCalendar); + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_CALENDER_DELETED") + .responseMessage("팀 일정 삭제 완료") + .build() + ); + } + + @ApiOperation(value = "팀 일정 단건 조회") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_CALENDER_FOUND", + content = @Content(schema = @Schema(implementation = TeamCalendarDefaultResponseDto.class))), + @ApiResponse(responseCode = "400", + description = "TEAMCALENDARID_NEGATIVEORZERO_INVALID"), + @ApiResponse(responseCode = "403", + description = "FORBIDDEN_TEAMCALENDAR"), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND / TEAMCALENDAR_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR"), + }) + @GetMapping("/api/v1/member/team/calendar") + public ResponseEntity> findTeamCalender( + HttpServletRequest servletRequest, + @RequestParam(value = "teamCalendarId") + @Positive(message = "조회할 팀일정 식별자는 양수만 가능합니다.") + Long teamCalendarId + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + + Team team = teamService.findByMember(member); + TeamCalendar teamCalendar = teamCalendarService.findById(teamCalendarId); + teamCalendarService.validateTeamCalendarAuthorization(team, teamCalendar); + + List targetMembers = teamCalendarService.validateTeamMemberExistenceForFind(teamCalendar); + + if (targetMembers == null) + throw new CustomException(null, TEAMCALENDAR_NOT_FOUND); + + + List childResponses = targetMembers.stream() + .map(m -> new TeamMemberFindResponseDto(m)).collect(Collectors.toList()); + + TeamCalendarDefaultResponseDto response = TeamCalendarDefaultResponseDto.builder() + .teamCalendar(teamCalendar) + .targets(childResponses) + .build(); + + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_CALENDER_FOUND") + .responseMessage("팀 일정 단건 조회 완료") + .data(response) + .build() + ); + } + + @ApiOperation(value = "팀 일정 월별 조회", notes = "- 종료일이 지난 일정도 전부 응답합니다.") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_CALENDERS_FOUND", + content = @Content(schema = @Schema(implementation = TeamCalendarAbstractResponseDto.class))), + @ApiResponse(responseCode = "400", + description = "YEARMONTH_FIELD_REQUIRED"), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR"), + }) + @PostMapping("/api/v1/member/team/calendars") + public ResponseEntity> findTeamCalenders( + HttpServletRequest servletRequest, + @RequestBody @Valid CalendarFindManyRequestDto request + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Team team = teamService.findByMember(member); + + List teamCalendars = teamCalendarService.findManyByYearMonth(team, request.getYearMonth()); + + List responses = new ArrayList<>(); + + for (TeamCalendar teamCalendar : teamCalendars) { + List targetMembers = teamCalendarService.validateTeamMemberExistenceForFind(teamCalendar); + + if (targetMembers == null) + continue; + + List childResponses = targetMembers.stream() + .map(targetMember -> new TeamMemberFindResponseDto(targetMember)) + .collect(Collectors.toList()); + + responses.add(new TeamCalendarAbstractResponseDto(teamCalendar, childResponses)); + } + + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_CALENDERS_FOUND") + .responseMessage("팀 일정 월별 조회 완료") + .data(responses) + .build() + ); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/controller/TeamController.java b/src/main/java/idorm/idormServer/calendar/controller/TeamController.java new file mode 100644 index 00000000..d5bddec7 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/controller/TeamController.java @@ -0,0 +1,208 @@ +package idorm.idormServer.calendar.controller; + +import idorm.idormServer.auth.JwtTokenProvider; +import idorm.idormServer.calendar.domain.Team; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindManyResponseDto; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindResponseDto; +import idorm.idormServer.calendar.service.CalendarServiceFacade; +import idorm.idormServer.calendar.service.TeamService; +import idorm.idormServer.common.DefaultResponseDto; +import idorm.idormServer.member.domain.Member; +import idorm.idormServer.member.service.MemberService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.AccessLevel; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.constraints.Positive; +import java.util.List; +import java.util.stream.Collectors; + +@Api(tags = "팀 관리") +@Validated +@RestController +@RequiredArgsConstructor(access = AccessLevel.PROTECTED) +public class TeamController { + + private final JwtTokenProvider jwtTokenProvider; + private final MemberService memberService; + private final TeamService teamService; + private final CalendarServiceFacade calendarServiceFacade; + + @ApiOperation(value = "룸메이트 초대 수락", notes = "- 초대를 보낼 때가 아닌, 초대를 수락할 때 요청을 보내주세요.") + @ApiResponses(value = { + @ApiResponse( + responseCode = "201", + description = "TEAM_MEMBER_CREATED", + content = @Content(schema = @Schema(implementation = Object.class))), + @ApiResponse(responseCode = "400", + description = "REGISTERMEMBERID_NEGATIVEORZERO_INVALID / ILLEGAL_STATEMENT_EXPLODEDTEAM"), + @ApiResponse(responseCode = "403", + description = "FORBIDDEN_TARGET_ADMIN"), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND"), + @ApiResponse(responseCode = "409", + description = "DUPLICATE_TEAM / TEAM_STATUS_FULL"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR") + }) + @PostMapping("/api/v1/member/team") + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity> addTeamMember( + HttpServletRequest servletRequest, + @RequestParam(value = "registerMemberId") + @Positive(message = "회원 식별자는 양수만 가능합니다.") + Long registerMemberId + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Member registerMember = memberService.findById(registerMemberId); + + memberService.validateTargetSelf(member, registerMember); + memberService.validateTargetAdmin(registerMember); + teamService.validateTeamExistence(registerMember); + + Team team = teamService.findByMemberOptional(member); + + calendarServiceFacade.addTeamMember(team, member, registerMember); + + return ResponseEntity.status(201) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_MEMBER_CREATED") + .responseMessage("팀 회원 초대 완료") + .build() + ); + } + + @ApiOperation(value = "팀 나가기") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_MEMBER_DELETED", + content = @Content(schema = @Schema(implementation = Object.class))), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND / TEAM_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR") + }) + @DeleteMapping("/api/v1/member/team") + public ResponseEntity> deleteTeamMember( + HttpServletRequest servletRequest + ) { + + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + + teamService.validateTeamNotExistence(member); + Team team = teamService.findByMember(member); + + calendarServiceFacade.deleteTeamMember(team, member); + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_MEMBER_DELETED") + .responseMessage("팀 회원 삭제 완료") + .build() + ); + } + + @ApiOperation(value = "팀원 전체 조회", notes = "- 팀에 소속된 팀원이 1명이라면, isNeedToConfirmDeleted: true 로 응답합니다. \n" + + "- 남은 팀원 1명이 팀 폭발 여부를 확인했다면, 팀 폭발 인지 OK 요청 을 서버에게 보내주세요. \n" + + "- 팀원이 없다면 팀 식별자(teamId)가 -999 이고, members는 빈 배열로 응답합니다. \n" + + "- json 내부의 회원 배열에서, 회원의 order는 0부터 시작합니다.") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_MEMBERS_FOUND", + content = @Content(schema = @Schema(implementation = TeamMemberFindManyResponseDto.class))), + @ApiResponse(responseCode = "404", + description = "MEMBER_NOT_FOUND"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR"), + }) + @GetMapping("/api/v1/member/team") + public ResponseEntity> findTeamMembers( + HttpServletRequest servletRequest + ) { + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Team team = teamService.findByMemberOptional(member); + + TeamMemberFindManyResponseDto responses = null; + + if (team == null) { + + responses = new TeamMemberFindManyResponseDto(-999L, + false, + null); + } else { + List members = teamService.findTeamMembers(team); + + if (members.size() < 2) { + teamService.updateIsNeedToConfirmDeleted(team); + responses = new TeamMemberFindManyResponseDto(team.getId(), + true, + null); + } else { + List childResponses = members.stream() + .map(m -> new TeamMemberFindResponseDto(m)).collect(Collectors.toList()); + + responses = new TeamMemberFindManyResponseDto(team.getId(), + false, + childResponses); + } + } + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_MEMBERS_FOUND") + .responseMessage("팀 회원 다건 조회 완료") + .data(responses) + .build() + ); + } + + @ApiOperation(value = "팀 폭발 확인 OK", notes = "- 최후의 남은 팀원 1명이 팀 폭발했음을 확인했을 때 해당 API를 요청해주세요.") + @ApiResponses(value = { + @ApiResponse( + responseCode = "200", + description = "TEAM_EXPLODED_CHECKED", + content = @Content(schema = @Schema(implementation = Object.class))), + @ApiResponse(responseCode = "409", + description = "CANNOT_EXPLODE_TEAM"), + @ApiResponse(responseCode = "500", + description = "SERVER_ERROR") + }) + @PatchMapping("/api/v1/member/team") + public ResponseEntity> isConfirmTeamExploded( + HttpServletRequest servletRequest + ) { + long memberId = Long.parseLong(jwtTokenProvider.getUsername(servletRequest.getHeader("X-AUTH-TOKEN"))); + Member member = memberService.findById(memberId); + Team team = teamService.findByMemberOptional(member); + + if (team == null) { + teamService.removeMember(team, member); + } else { + teamService.validateReadyToDeleteTeam(team); + teamService.delete(team); + } + + return ResponseEntity.status(200) + .body(DefaultResponseDto.builder() + .responseCode("TEAM_EXPLODED_CHECKED") + .responseMessage("팀 삭제 완료") + .build() + ); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/domain/Team.java b/src/main/java/idorm/idormServer/calendar/domain/Team.java new file mode 100644 index 00000000..b4d266e1 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/domain/Team.java @@ -0,0 +1,93 @@ +package idorm.idormServer.calendar.domain; + +import idorm.idormServer.common.BaseEntity; +import idorm.idormServer.member.domain.Member; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Team extends BaseEntity { + + @Id + @Column(name = "team_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Boolean isNeedToConfirmDeleted; // 최후의 1인이 팀 폭발여부 확인했는지 여부 + + @OneToMany(mappedBy = "team") + private List members = new ArrayList<>(); + + @OneToMany(mappedBy = "team") + private List teamCalendars = new ArrayList<>(); + + @Builder + public Team(Member member) { + addMember(member); + member.updateTeam(this, 0); + this.isNeedToConfirmDeleted = false; + this.setIsDeleted(false); + } + + public void addMember(Member member) { + if (!this.members.contains(member)) { + this.members.add(member); + member.updateTeam(this, this.members.size() - 1); + } + } + + public boolean removeMember(Member member) { + if (this.members.contains(member)) { + this.members.remove(member); + member.deleteTeam(this); + return true; + } + return false; + } + + public List getMembers() { + return this.members; + } + + public int getMemberCount() { + return this.members.size(); + } + + public void addTeamCalendar(TeamCalendar teamCalendar) { + this.teamCalendars.add(teamCalendar); + } + + public void removeTeamCalendar(TeamCalendar teamCalendar) { + this.teamCalendars.remove(teamCalendar); + } + + public List getTeamCalendars() { + List teamCalendarList = this.teamCalendars; + teamCalendarList.removeIf(teamCalendar -> teamCalendar.getIsDeleted().equals(true)); + return teamCalendarList; + } + + public void updateIsNeedToConfirmDeleted() { + this.isNeedToConfirmDeleted = true; + } + + public void delete() { + this.setIsDeleted(true); + + for (Member member : this.members) + member.deleteTeam(this); + + List deleteList = this.getTeamCalendars().stream().collect(Collectors.toList()); + for (TeamCalendar teamCalendar : deleteList) + teamCalendar.delete(); + + } +} diff --git a/src/main/java/idorm/idormServer/calendar/domain/TeamCalendar.java b/src/main/java/idorm/idormServer/calendar/domain/TeamCalendar.java new file mode 100644 index 00000000..b988b522 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/domain/TeamCalendar.java @@ -0,0 +1,97 @@ +package idorm.idormServer.calendar.domain; + +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarUpdateRequestDto; +import idorm.idormServer.common.BaseEntity; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class TeamCalendar extends BaseEntity { + + @Id + @Column(name = "team_calendar_id") + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private LocalDate startDate; + private LocalDate endDate; + private LocalTime startTime; + private LocalTime endTime; + private String title; + private String content; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "team_id") + private Team team; + + @ElementCollection + @CollectionTable(name = "targets", + joinColumns = @JoinColumn(name = "team_calendar_id")) + @Column(name = "team_calendar_target") + private List targets = new ArrayList<>(); // 일정 대상자들 + + @Builder + public TeamCalendar(LocalDate startDate, + LocalDate endDate, + LocalTime startTime, + LocalTime endTime, + String title, + String content, + Team team, + List targets) { + this.startDate = startDate; + this.endDate = endDate; + this.startTime = startTime; + this.endTime = endTime; + this.title = title; + this.content = content; + + for (Long target : targets) + this.targets.add(target); + + this.setIsDeleted(false); + + this.team = team; + + if (!team.getTeamCalendars().contains(this)) + team.addTeamCalendar(this); + } + + public void updateContents(TeamCalendarUpdateRequestDto request, List targets) { + this.startDate = request.getStartDate(); + this.endDate = request.getEndDate(); + this.startTime = request.getStartTime(); + this.endTime = request.getEndTime(); + this.title = request.getTitle(); + this.content = request.getContent(); + + this.updateTargets(targets); + } + + private void updateTargets(List newTargets) { + this.targets.clear(); + for (Long target : newTargets) + this.targets.add(target); + } + + public void deleteTarget(Long target) { + if (this.targets.contains(target)) + this.targets.remove(target); + } + + public void delete() { + this.setIsDeleted(true); + if (team.getTeamCalendars().contains(this)) + this.team.removeTeamCalendar(this); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarAdminResponseDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarAdminResponseDto.java index 5bc5b523..d6cb5e4f 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarAdminResponseDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarAdminResponseDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import com.fasterxml.jackson.annotation.JsonFormat; import idorm.idormServer.calendar.domain.Calendar; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarDefaultResponseDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarDefaultResponseDto.java index fc8e9d53..60ac9696 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarDefaultResponseDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarDefaultResponseDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import com.fasterxml.jackson.annotation.JsonFormat; import idorm.idormServer.calendar.domain.Calendar; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarFindManyRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarFindManyRequestDto.java index 3bce4292..b3f919cf 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarFindManyRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarFindManyRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import com.fasterxml.jackson.annotation.JsonFormat; import idorm.idormServer.common.ValidationSequence; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarSaveRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarSaveRequestDto.java index 7727b139..c8021579 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarSaveRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarSaveRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import com.fasterxml.jackson.annotation.JsonFormat; import idorm.idormServer.calendar.domain.Calendar; @@ -25,7 +25,7 @@ ValidationSequence.NotNull.class, ValidationSequence.Size.class, }) -@ApiModel(value = "일정 저장 요청") +@ApiModel(value = "공식 일정 저장 요청") public class CalendarSaveRequestDto { @ApiModelProperty(position = 1, required = true, value = "1기숙사 대상 여부", allowableValues = "true, false", @@ -51,11 +51,11 @@ public class CalendarSaveRequestDto { @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") private LocalDate endDate; - @ApiModelProperty(position = 6, notes = "string", value = "시작시간", example = "15:40:00") + @ApiModelProperty(position = 6, notes = "string", value = "시작시간", example = "15:00:00") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") private LocalTime startTime; - @ApiModelProperty(position = 6, notes = "string", value = "종료시간", example = "16:50:00") + @ApiModelProperty(position = 6, notes = "string", value = "종료시간", example = "16:00:00") @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") private LocalTime endTime; @ApiModelProperty(position = 8, required = true, value = "내용", example = "기숙사 화재 훈련") diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateContentRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateContentRequestDto.java index 87526365..06f45671 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateContentRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateContentRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import idorm.idormServer.common.ValidationSequence; import io.swagger.annotations.ApiModel; @@ -17,15 +17,16 @@ @AllArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED) @GroupSequence({CalendarUpdateContentRequestDto.class, - ValidationSequence.NotNull.class, ValidationSequence.NotBlank.class, + ValidationSequence.NotNull.class, + ValidationSequence.Positive.class }) @ApiModel(value = "일정 수정 요청 - 내용") public class CalendarUpdateContentRequestDto { @ApiModelProperty(position = 1, required = true, value="캘린더 식별자", example = "1") - @Positive(message = "수정할 일정 식별자는 양수만 가능합니다.") - @NotNull(message = "내용을 입력해 주세요.", groups = ValidationSequence.NotNull.class) + @Positive(message = "수정할 일정 식별자는 양수만 가능합니다.", groups = ValidationSequence.Positive.class) + @NotNull(message = "일정 식별자를 입력해 주세요.", groups = ValidationSequence.NotNull.class) private Long calendarId; @ApiModelProperty(position = 2, required = true, example = "기숙사 화재 훈련", value = "내용") diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateIsDormYnRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateIsDormYnRequestDto.java index bd2745d0..0eac9a68 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateIsDormYnRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateIsDormYnRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import idorm.idormServer.common.ValidationSequence; import io.swagger.annotations.ApiModel; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateLocationRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateLocationRequestDto.java index 363b811e..f2316217 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateLocationRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateLocationRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import idorm.idormServer.common.ValidationSequence; import io.swagger.annotations.ApiModel; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndDateRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndDateRequestDto.java index e742441f..6c1443d6 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndDateRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndDateRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import com.fasterxml.jackson.annotation.JsonFormat; import idorm.idormServer.common.ValidationSequence; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndTimeRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndTimeRequestDto.java index 26f168cd..d8b80f8e 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndTimeRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateStartAndEndTimeRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import com.fasterxml.jackson.annotation.JsonFormat; import idorm.idormServer.common.ValidationSequence; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateUrlRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateUrlRequestDto.java index fb4a1a41..cfbbbee0 100644 --- a/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateUrlRequestDto.java +++ b/src/main/java/idorm/idormServer/calendar/dto/Calendar/CalendarUpdateUrlRequestDto.java @@ -1,4 +1,4 @@ -package idorm.idormServer.calendar.dto; +package idorm.idormServer.calendar.dto.Calendar; import idorm.idormServer.common.ValidationSequence; import io.swagger.annotations.ApiModel; diff --git a/src/main/java/idorm/idormServer/calendar/dto/Team/TeamMemberFindManyResponseDto.java b/src/main/java/idorm/idormServer/calendar/dto/Team/TeamMemberFindManyResponseDto.java new file mode 100644 index 00000000..be1bb670 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/dto/Team/TeamMemberFindManyResponseDto.java @@ -0,0 +1,36 @@ +package idorm.idormServer.calendar.dto.Team; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ApiModel(value = "팀 회원 다건 조회 응답") +public class TeamMemberFindManyResponseDto { + + @ApiModelProperty(position = 1, required = true, value= "팀 식별자", example = "1") + private Long teamId; + + @ApiModelProperty(position = 2, required = true, value= "팀 폭발 확인 필요 여부", example = "false") + private Boolean isNeedToConfirmDeleted; + + @ApiModelProperty(position = 3, required = true, value= "팀 회원들") + private List members = new ArrayList<>(); + + public TeamMemberFindManyResponseDto(Long teamId, + Boolean isNeedToConfirmDeleted, + List members) { + this.teamId = teamId; + this.isNeedToConfirmDeleted = isNeedToConfirmDeleted; + + if (members != null) + this.members = members.stream().collect(Collectors.toList()); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/dto/Team/TeamMemberFindResponseDto.java b/src/main/java/idorm/idormServer/calendar/dto/Team/TeamMemberFindResponseDto.java new file mode 100644 index 00000000..596034aa --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/dto/Team/TeamMemberFindResponseDto.java @@ -0,0 +1,36 @@ +package idorm.idormServer.calendar.dto.Team; + +import idorm.idormServer.member.domain.Member; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@ApiModel(value = "팀 회원 조회 응답") +public class TeamMemberFindResponseDto { + + @ApiModelProperty(position = 1, required = true, value= "회원 등록 순서", example = "1") + private int order; + @ApiModelProperty(position = 2, required = true, value= "회원 식별자", example = "10") + private Long memberId; + + @ApiModelProperty(position = 3, required = true, value = "닉네임", example = "도미") + private String nickname; + + @ApiModelProperty(position = 4, value = "프로필사진 주소", example = "사진 url") + private String profilePhotoUrl; + + public TeamMemberFindResponseDto(Member member) { // 탈퇴 회원인지, 팀에 소속되어있는지 체크 후 호출하기 + this.order = member.getTeamOrder(); + this.memberId = member.getId(); + this.nickname = member.getNickname(); + + if (member.getMemberPhoto() != null) + this.profilePhotoUrl = member.getMemberPhoto().getPhotoUrl(); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarAbstractResponseDto.java b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarAbstractResponseDto.java new file mode 100644 index 00000000..e4c3b7ea --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarAbstractResponseDto.java @@ -0,0 +1,47 @@ +package idorm.idormServer.calendar.dto.TeamCalendar; + +import com.fasterxml.jackson.annotation.JsonFormat; +import idorm.idormServer.calendar.domain.TeamCalendar; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindResponseDto; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ApiModel(value = "팀 일정 요약 응답") +public class TeamCalendarAbstractResponseDto { + + @ApiModelProperty(position = 1, required = true, value = "팀일정 식별자", example = "1") + private Long teamCalendarId; + + @ApiModelProperty(position = 2, required = true, value = "내용", example = "청소") + private String title; + + @ApiModelProperty(position = 4, notes = "string", value = "시작일자", example = "2023-04-27") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate startDate; + + @ApiModelProperty(position = 8, required = true, value = "일정 대상자의 식별자") + private List targets = new ArrayList<>(); + + @Builder + public TeamCalendarAbstractResponseDto(TeamCalendar teamCalendar, List targets) { + this.teamCalendarId = teamCalendar.getId(); + this.title = teamCalendar.getTitle(); + + if (teamCalendar.getStartDate() != null) + this.startDate = teamCalendar.getStartDate(); + + if (targets != null) + this.targets = targets.stream().collect(Collectors.toList()); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarDefaultResponseDto.java b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarDefaultResponseDto.java new file mode 100644 index 00000000..c4dc4803 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarDefaultResponseDto.java @@ -0,0 +1,68 @@ +package idorm.idormServer.calendar.dto.TeamCalendar; + +import com.fasterxml.jackson.annotation.JsonFormat; +import idorm.idormServer.calendar.domain.TeamCalendar; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindResponseDto; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.*; + +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@ApiModel(value = "팀 일정 기본 응답") +public class TeamCalendarDefaultResponseDto { + + @ApiModelProperty(position = 1, required = true, value = "팀일정 식별자", example = "1") + private Long teamCalendarId; + + @ApiModelProperty(position = 2, required = true, value = "내용", example = "청소") + private String title; + + @ApiModelProperty(position = 3, value = "내용", example = "방 청소만 하는게 아니라 화장실거울까지!") + private String content; + + @ApiModelProperty(position = 4, notes = "string", value = "시작일자", example = "2023-04-27") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate startDate; + + @ApiModelProperty(position = 5, notes = "string", value = "종료일자", example = "2023-04-28") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate endDate; + + @ApiModelProperty(position = 6, notes = "string", value = "시작시간", example = "15:00:00") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") + private LocalTime startTime; + + @ApiModelProperty(position = 7, notes = "string", value = "종료시간", example = "16:00:00") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") + private LocalTime endTime; + + @ApiModelProperty(position = 8, required = true, value = "일정 대상자의 식별자") + private List targets = new ArrayList<>(); + + @Builder + public TeamCalendarDefaultResponseDto(TeamCalendar teamCalendar, List targets) { + this.teamCalendarId = teamCalendar.getId(); + this.title = teamCalendar.getTitle(); + if (teamCalendar.getContent() != null) + this.content = teamCalendar.getContent(); + if (teamCalendar.getStartDate() != null) + this.startDate = teamCalendar.getStartDate(); + if (teamCalendar.getEndDate() != null) + this.endDate = teamCalendar.getEndDate(); + if (teamCalendar.getStartTime() != null) + this.startTime = teamCalendar.getStartTime(); + if (teamCalendar.getEndTime() != null) + this.endTime = teamCalendar.getEndTime(); + + if (targets != null) + this.targets = targets.stream().collect(Collectors.toList()); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarSaveRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarSaveRequestDto.java new file mode 100644 index 00000000..1b8c44a2 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarSaveRequestDto.java @@ -0,0 +1,73 @@ +package idorm.idormServer.calendar.dto.TeamCalendar; + +import com.fasterxml.jackson.annotation.JsonFormat; +import idorm.idormServer.calendar.domain.Team; +import idorm.idormServer.calendar.domain.TeamCalendar; +import idorm.idormServer.common.ValidationSequence; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.GroupSequence; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.Size; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@GroupSequence({TeamCalendarSaveRequestDto.class, + ValidationSequence.NotBlank.class, + ValidationSequence.NotNull.class, + ValidationSequence.Size.class +}) +@ApiModel(value = "팀 일정 저장 요청") +public class TeamCalendarSaveRequestDto { + + @ApiModelProperty(position = 1, required = true, value = "내용", example = "청소") + @NotBlank(message = "내용을 입력해 주세요.", groups = ValidationSequence.NotBlank.class) + @Size(max = 15, message = "제목은 ~15자 이내여야 합니다.", groups = ValidationSequence.Size.class) + private String title; + + @ApiModelProperty(position = 2, value = "내용", example = "방 청소만 하는게 아니라 화장실거울까지!") + @Size(max = 100, message = "내용은 ~100자 이내여야 합니다.", groups = ValidationSequence.Size.class) + private String content; + + @ApiModelProperty(position = 3, notes = "string", value = "시작일자", example = "2023-04-27") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate startDate; + + @ApiModelProperty(position = 4, notes = "string", value = "종료일자", example = "2023-04-28") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate endDate; + + @ApiModelProperty(position = 5, notes = "string", value = "시작시간", example = "15:00:00") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") + private LocalTime startTime; + + @ApiModelProperty(position = 6, notes = "string", value = "종료시간", example = "16:00:00") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") + private LocalTime endTime; + + @ApiModelProperty(position = 7, value = "일정 대상자의 식별자") + private List targets = new ArrayList<>(); + + public TeamCalendar toEntity(Team team) { + return TeamCalendar.builder() + .team(team) + .targets(this.targets) + .title(title) + .content(content) + .startDate(this.startDate) + .endDate(this.endDate) + .startTime(this.startTime) + .endTime(this.endTime) + .build(); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarUpdateRequestDto.java b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarUpdateRequestDto.java new file mode 100644 index 00000000..3c564f79 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/dto/TeamCalendar/TeamCalendarUpdateRequestDto.java @@ -0,0 +1,67 @@ +package idorm.idormServer.calendar.dto.TeamCalendar; + +import com.fasterxml.jackson.annotation.JsonFormat; +import idorm.idormServer.common.ValidationSequence; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.GroupSequence; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Positive; +import javax.validation.constraints.Size; +import java.time.LocalDate; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.List; + +@Getter +@AllArgsConstructor +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@GroupSequence({TeamCalendarUpdateRequestDto.class, + ValidationSequence.NotBlank.class, + ValidationSequence.NotNull.class, + ValidationSequence.Size.class, + ValidationSequence.Positive.class +}) +@ApiModel(value = "팀 일정 수정 요청") +public class TeamCalendarUpdateRequestDto { + + @ApiModelProperty(position = 1, required = true, value="팀 일정 식별자", example = "1") + @NotNull(message = "내용을 입력해 주세요.", groups = ValidationSequence.NotNull.class) + @Positive(message = "수정할 팀 일정 식별자는 양수만 가능합니다.") + private Long teamCalendarId; + + @ApiModelProperty(position = 2, required = true, value = "내용", example = "청소") + @NotBlank(message = "내용을 입력해 주세요.", groups = ValidationSequence.NotBlank.class) + @Size(max = 15, message = "제목은 ~15자 이내여야 합니다.", groups = ValidationSequence.Size.class) + private String title; + + @ApiModelProperty(position = 3, value = "내용", example = "방 청소만 하는게 아니라 화장실거울까지!") + @Size(max = 100, message = "내용은 ~100자 이내여야 합니다.", groups = ValidationSequence.Size.class) + private String content; + + @ApiModelProperty(position = 4, notes = "string", value = "시작일자", example = "2023-04-27") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate startDate; + + @ApiModelProperty(position = 5, notes = "string", value = "종료일자", example = "2023-04-28") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") + private LocalDate endDate; + + @ApiModelProperty(position = 6, notes = "string", value = "시작시간", example = "15:00:00") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") + private LocalTime startTime; + + @ApiModelProperty(position = 7, notes = "string", value = "종료시간", example = "16:00:00") + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss") + private LocalTime endTime; + + @ApiModelProperty(position = 8, value = "일정 대상자의 식별자") + @NotNull(message = "대상자의 식별자를 입력해주세요.", groups = ValidationSequence.NotNull.class) + private List targets = new ArrayList<>(); +} diff --git a/src/main/java/idorm/idormServer/calendar/repository/CalendarRepository.java b/src/main/java/idorm/idormServer/calendar/repository/CalendarRepository.java index d6591ab5..200fd47c 100644 --- a/src/main/java/idorm/idormServer/calendar/repository/CalendarRepository.java +++ b/src/main/java/idorm/idormServer/calendar/repository/CalendarRepository.java @@ -16,9 +16,10 @@ public interface CalendarRepository extends JpaRepository { @Query(value = "SELECT * " + "FROM calendar c " + - "WHERE c.start_date LIKE :yearMonth " + + "WHERE (c.start_date LIKE :yearMonth " + + "OR c.end_date LIKE :yearMonth) " + "AND c.is_deleted = 0", nativeQuery = true) - List findByIsDeletedIsFalseAndStartDateLike(String yearMonth); + List findByIsDeletedIsFalseAndDateLike(String yearMonth); @Query(value = "SELECT * " + "FROM calendar c " + diff --git a/src/main/java/idorm/idormServer/calendar/repository/TeamCalendarRepository.java b/src/main/java/idorm/idormServer/calendar/repository/TeamCalendarRepository.java new file mode 100644 index 00000000..e263cc85 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/repository/TeamCalendarRepository.java @@ -0,0 +1,21 @@ +package idorm.idormServer.calendar.repository; + +import idorm.idormServer.calendar.domain.TeamCalendar; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; + +import java.util.List; +import java.util.Optional; + +public interface TeamCalendarRepository extends JpaRepository { + + Optional findByIdAndIsDeletedIsFalse(Long id); + + @Query(value = "SELECT * " + + "FROM team_calendar c " + + "WHERE c.team_id = :teamId " + + "AND (c.start_date LIKE :yearMonth " + + "OR c.end_date LIKE :yearMonth) " + + "AND c.is_deleted = 0", nativeQuery = true) + List findByTeamAndIsDeletedIsFalseAndDateLike(Long teamId, String yearMonth); +} diff --git a/src/main/java/idorm/idormServer/calendar/repository/TeamRepository.java b/src/main/java/idorm/idormServer/calendar/repository/TeamRepository.java new file mode 100644 index 00000000..a8476ba2 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/repository/TeamRepository.java @@ -0,0 +1,7 @@ +package idorm.idormServer.calendar.repository; + +import idorm.idormServer.calendar.domain.Team; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TeamRepository extends JpaRepository { +} diff --git a/src/main/java/idorm/idormServer/calendar/service/CalendarService.java b/src/main/java/idorm/idormServer/calendar/service/CalendarService.java index bbf3f615..e4358d50 100644 --- a/src/main/java/idorm/idormServer/calendar/service/CalendarService.java +++ b/src/main/java/idorm/idormServer/calendar/service/CalendarService.java @@ -1,7 +1,7 @@ package idorm.idormServer.calendar.service; import idorm.idormServer.calendar.domain.Calendar; -import idorm.idormServer.calendar.dto.*; +import idorm.idormServer.calendar.dto.Calendar.*; import idorm.idormServer.calendar.repository.CalendarRepository; import idorm.idormServer.exception.CustomException; import lombok.RequiredArgsConstructor; @@ -139,7 +139,7 @@ public List findMany() { public List findManyByYearMonth(YearMonth yearMonth) { try { - return calendarRepository.findByIsDeletedIsFalseAndStartDateLike(yearMonth + "-%"); + return calendarRepository.findByIsDeletedIsFalseAndDateLike(yearMonth + "-%"); } catch (RuntimeException e) { throw new CustomException(e, SERVER_ERROR); } diff --git a/src/main/java/idorm/idormServer/calendar/service/CalendarServiceFacade.java b/src/main/java/idorm/idormServer/calendar/service/CalendarServiceFacade.java new file mode 100644 index 00000000..061536c3 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/service/CalendarServiceFacade.java @@ -0,0 +1,36 @@ +package idorm.idormServer.calendar.service; + +import idorm.idormServer.calendar.domain.Team; +import idorm.idormServer.member.domain.Member; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +public class CalendarServiceFacade { + + private final TeamService teamService; + + public void addTeamMember(Team team, Member loginMember, Member registerMember) { + if (team != null) { // 룸메이트 초대 + teamService.validateTeamFull(team); + teamService.validateIsDeletedTeam(team); + teamService.addMember(team, registerMember); + } else { // 팀 생성 후 룸메이트 초대 + Team createdTeam = teamService.create(loginMember); + teamService.addMember(createdTeam, registerMember); + } + } + + public void deleteTeamMember(Team team, Member member) { + teamService.removeMember(team, member); + + if (team.getMemberCount() < 1) { // 팀 삭제 + teamService.delete(team); + } else if (team.getMemberCount() == 1) { // 팀 폭발여부 변경 + teamService.updateIsNeedToConfirmDeleted(team); + } + } +} diff --git a/src/main/java/idorm/idormServer/calendar/service/TeamCalendarService.java b/src/main/java/idorm/idormServer/calendar/service/TeamCalendarService.java new file mode 100644 index 00000000..f54d5eb5 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/service/TeamCalendarService.java @@ -0,0 +1,178 @@ +package idorm.idormServer.calendar.service; + +import idorm.idormServer.calendar.domain.Team; +import idorm.idormServer.calendar.domain.TeamCalendar; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarUpdateRequestDto; +import idorm.idormServer.calendar.repository.TeamCalendarRepository; +import idorm.idormServer.exception.CustomException; +import idorm.idormServer.member.domain.Member; +import idorm.idormServer.member.service.MemberService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.YearMonth; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static idorm.idormServer.exception.ExceptionCode.*; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class TeamCalendarService { + + private final TeamCalendarRepository teamCalendarRepository; + private final MemberService memberService; + private final TeamService teamService; + + /** + * DB에 팀일정 저장 | + * 500(SERVER_ERROR) + */ + @Transactional + public TeamCalendar save(TeamCalendar teamCalendar) { + try { + return teamCalendarRepository.save(teamCalendar); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀일정 수정 | + */ + @Transactional + public void update(TeamCalendar teamCalendar, + TeamCalendarUpdateRequestDto request, + List targets) { + try { + teamCalendar.updateContents(request, targets); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀일정 삭제 | + * 500(SERVER_ERROR) + */ + @Transactional + public void delete(TeamCalendar teamCalendar) { + try { + teamCalendar.delete(); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀일정 대상 삭제 | + * 500(SERVER_ERROR) + */ + @Transactional + public void deleteTarget(TeamCalendar teamCalendar, Long memberId) { + try { + teamCalendar.deleteTarget(memberId); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀일정 월별 조회 | + * 500(SERVER_ERROR) + */ + public List findManyByYearMonth(Team team, YearMonth yearMonth) { + try { + return teamCalendarRepository.findByTeamAndIsDeletedIsFalseAndDateLike(team.getId(), + yearMonth + "-%"); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀일정 단건 조회 | + * 404(TEAMCALENDAR_NOT_FOUND) + */ + public TeamCalendar findById(Long teamCalendarId) { + return teamCalendarRepository.findByIdAndIsDeletedIsFalse(teamCalendarId) + .orElseThrow(() -> { + throw new CustomException(null, TEAMCALENDAR_NOT_FOUND); + }); + } + + /** + * 팀일정 대상자의 팀 존재 여부 검증 | + * 저장, 수정, 삭제 시 한다. | + * 404(TEAMMEMBER_NOT_FOUND) + */ + public List validateTeamMemberExistence(Team team, List targets) { + List teamMembers = team.getMembers(); + List targetMembers = new ArrayList<>(); + + for (Long target : targets) { + Member targetMember = memberService.findById(target); + if (!teamMembers.contains(targetMember)) + throw new CustomException(null, TEAMMEMBER_NOT_FOUND); + targetMembers.add(targetMember); + } + return targetMembers; + } + + /** + * 팀일정 대상자의 팀 존재 여부 검증 | + * 조회 시 사용한다. 더이상 존재하지 않는 대상자라면, 해당 대상자 삭제 후 응답한다. + * 해당 팀일정에 대상자가 존재하지 않으면, 팀일정을 삭제 후 null을 리턴한다. + * 404(DELETED_TEAMCALENDAR) + */ + @Transactional + public List validateTeamMemberExistenceForFind(TeamCalendar teamCalendar) { + List targets = teamCalendar.getTargets().stream().collect(Collectors.toList()); + List teamMembers = teamCalendar.getTeam().getMembers(); + List returnMembers = new ArrayList<>(); + + for (Long target : targets) { + Optional targetMember = memberService.findByIdOptional(target); + + if (targetMember.isEmpty()) { + this.deleteTarget(teamCalendar, target); + continue; + } + + if (!teamMembers.contains(targetMember.get())) { + this.deleteTarget(teamCalendar, target); + continue; + } + returnMembers.add(targetMember.get()); + } + + if (returnMembers.size() < 1) { + this.delete(teamCalendar); + return null; + } + + return returnMembers; + } + + /** + * 팀일정 수정 권한 검증 | + * 403(FORBIDDEN_TEAMCALENDAR) + */ + public void validateTeamCalendarAuthorization(Team team, TeamCalendar teamCalendar) { + if (!teamCalendar.getTeam().equals(team)) + throw new CustomException(null, FORBIDDEN_TEAMCALENDAR); + } + + /** + * 대상자 존재 여부 검증 | + * 400(TARGETS_FIELD_REQUIRED) + */ + public void validateTargetExistence(List targets) { + if (targets.size() < 1) + throw new CustomException(null, TARGETS_FIELD_REQUIRED); + } +} diff --git a/src/main/java/idorm/idormServer/calendar/service/TeamService.java b/src/main/java/idorm/idormServer/calendar/service/TeamService.java new file mode 100644 index 00000000..1f91e4d2 --- /dev/null +++ b/src/main/java/idorm/idormServer/calendar/service/TeamService.java @@ -0,0 +1,186 @@ +package idorm.idormServer.calendar.service; + +import idorm.idormServer.calendar.domain.Team; +import idorm.idormServer.calendar.repository.TeamRepository; +import idorm.idormServer.exception.CustomException; +import idorm.idormServer.member.domain.Member; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +import static idorm.idormServer.exception.ExceptionCode.*; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class TeamService { + + private final TeamRepository teamRepository; + + /** + * DB에 팀 저장 | + * 500(SERVER_ERROR) + */ + @Transactional + public Team save(Team team) { + try { + return teamRepository.save(team); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀 생성 | + */ + @Transactional + public Team create(Member member) { + Team team = Team.builder() + .member(member) + .build(); + return this.save(team); + } + + /** + * 팀 삭제 | + * 500(SERVER_ERROR) + */ + @Transactional + public void delete(Team team) { + try { + team.delete(); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀원 추가 | + * 500(SERVER_ERROR) + */ + @Transactional + public void addMember(Team team, Member member) { + try { + team.addMember(member); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀원 삭제 | + * 500(SERVER_ERROR) + */ + @Transactional + public void removeMember(Team team, Member member) { + try { + boolean result = team.removeMember(member); + if (result) { + AtomicInteger index = new AtomicInteger(); + for (Member remainMembers : team.getMembers()) + remainMembers.updateTeamOrder(team, index.getAndIncrement()); + } + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀 폭발 여부 수정 | + * 500(SERVER_ERROR) + */ + @Transactional + public void updateIsNeedToConfirmDeleted(Team team) { + try { + team.updateIsNeedToConfirmDeleted(); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀원으로 팀 조회 Optional | + */ + public Team findByMemberOptional(Member member) { + return member.getTeam(); + } + + /** + * 팀원으로 팀 조회 | + * 500(TEAM_NOT_FOUND) + */ + public Team findByMember(Member member) { + Team team = member.getTeam(); + if (team == null) + throw new CustomException(null, TEAM_NOT_FOUND); + else + return team; + } + + /** + * 팀원 전체 조회 | + * 500(SERVER_ERROR) + */ + @Transactional + public List findTeamMembers(Team team) { + + try { + List members = team.getMembers(); + for (Member member : members) { + if (member.getIsDeleted()) + team.removeMember(member); + } + return members; + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + + /** + * 팀원 만석 여부 검증 | + * 409(TEAM_STATUS_FULL) + */ + public void validateTeamFull(Team team) { + if (team.getMemberCount() >= 4) + throw new CustomException(null, TEAM_STATUS_FULL); + } + + /** + * 등록된 팀 존재 여부 검증 | + * 409(DUPLICATE_TEAM) + */ + public void validateTeamExistence(Member member) { + if (member.getTeam() != null) + throw new CustomException(null, DUPLICATE_TEAM); + } + + /** + * 등록된 팀 미존재 여부 검증 | + * 409(TEAM_NOT_FOUND) + */ + public void validateTeamNotExistence(Member member) { + if (member.getTeam() == null) + throw new CustomException(null, TEAM_NOT_FOUND); + } + + /** + * 팀 폭발 가능 여부 검증 | + * 409(CANNOT_EXPLODE_TEAM) + */ + public void validateReadyToDeleteTeam(Team team) { + if (!team.getIsNeedToConfirmDeleted()) + throw new CustomException(null, CANNOT_EXPLODE_TEAM); + } + + /** + * 팀 폭발 여부 검증 | + * 400(ILLEGAL_STATEMENT_EXPLODEDTEAM) + */ + public void validateIsDeletedTeam(Team team) { + if (team.getIsNeedToConfirmDeleted()) + throw new CustomException(null, ILLEGAL_STATEMENT_EXPLODEDTEAM); + } +} diff --git a/src/main/java/idorm/idormServer/config/SecurityConfiguration.java b/src/main/java/idorm/idormServer/config/SecurityConfiguration.java index 83479252..0e7031da 100644 --- a/src/main/java/idorm/idormServer/config/SecurityConfiguration.java +++ b/src/main/java/idorm/idormServer/config/SecurityConfiguration.java @@ -49,6 +49,8 @@ protected void configure(HttpSecurity http) throws Exception { .antMatchers("/email/**", "/verifyCode/**").permitAll() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/member/**").hasRole("USER") + .antMatchers("/**/admin/**").hasRole("ADMIN") + .antMatchers("/**/member/**").hasRole("USER") .and() .exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler()) diff --git a/src/main/java/idorm/idormServer/config/SwaggerConfiguration.java b/src/main/java/idorm/idormServer/config/SwaggerConfiguration.java index 1390d737..b2009758 100644 --- a/src/main/java/idorm/idormServer/config/SwaggerConfiguration.java +++ b/src/main/java/idorm/idormServer/config/SwaggerConfiguration.java @@ -1,8 +1,12 @@ package idorm.idormServer.config; import com.fasterxml.classmate.TypeResolver; -import idorm.idormServer.calendar.dto.CalendarAdminResponseDto; -import idorm.idormServer.calendar.dto.CalendarDefaultResponseDto; +import idorm.idormServer.calendar.dto.Calendar.CalendarAdminResponseDto; +import idorm.idormServer.calendar.dto.Calendar.CalendarDefaultResponseDto; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindManyResponseDto; +import idorm.idormServer.calendar.dto.Team.TeamMemberFindResponseDto; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarAbstractResponseDto; +import idorm.idormServer.calendar.dto.TeamCalendar.TeamCalendarDefaultResponseDto; import idorm.idormServer.community.dto.comment.CommentDefaultResponseDto; import idorm.idormServer.community.dto.comment.CommentParentResponseDto; import idorm.idormServer.community.dto.post.PostAbstractResponseDto; @@ -43,8 +47,12 @@ public Docket api() { typeResolver.resolve(CommentDefaultResponseDto.class), typeResolver.resolve(CommentParentResponseDto.class), typeResolver.resolve(CalendarDefaultResponseDto.class), - typeResolver.resolve(CalendarAdminResponseDto.class) - ) + typeResolver.resolve(CalendarAdminResponseDto.class), + typeResolver.resolve(TeamMemberFindManyResponseDto.class), + typeResolver.resolve(TeamMemberFindResponseDto.class), + typeResolver.resolve(TeamCalendarDefaultResponseDto.class), + typeResolver.resolve(TeamCalendarAbstractResponseDto.class) + ) .useDefaultResponseMessages(false) .select() .apis(RequestHandlerSelectors.withClassAnnotation(RestController.class)) diff --git a/src/main/java/idorm/idormServer/exception/ExceptionCode.java b/src/main/java/idorm/idormServer/exception/ExceptionCode.java index 759c484b..c1a72438 100644 --- a/src/main/java/idorm/idormServer/exception/ExceptionCode.java +++ b/src/main/java/idorm/idormServer/exception/ExceptionCode.java @@ -14,6 +14,7 @@ public enum ExceptionCode { * 400 BAD_REQUEST : 잘못된 요청 */ FIELD_REQUIRED(BAD_REQUEST, "입력은 필수 입니다."), + TARGETS_FIELD_REQUIRED(BAD_REQUEST, "대상자 입력은 필수 입니다."), // 형식 EMAIL_CHARACTER_INVALID(BAD_REQUEST, "올바른 형식의 이메일이 아닙니다."), @@ -44,6 +45,7 @@ public enum ExceptionCode { ILLEGAL_ARGUMENT_SELF(BAD_REQUEST, "본인은 해당 요청의 설정 대상이 될 수 없습니다."), ILLEGAL_STATEMENT_MATCHINGINFO_NON_PUBLIC(BAD_REQUEST, "매칭정보가 비공개 입니다."), + ILLEGAL_STATEMENT_EXPLODEDTEAM(CONFLICT, "폭발한 팀은 요청 대상이 될 수 없습니다."), DATE_SET_INVALID(BAD_REQUEST, "시작일자가 종료일자보다 빠르거나 같아야 합니다."), /** @@ -63,6 +65,8 @@ public enum ExceptionCode { * 403 FORBIDDEN : 권한이 없는 사용자 */ FORBIDDEN_AUTHORIZATION(FORBIDDEN, "접근 권한이 없습니다."), + FORBIDDEN_TARGET_ADMIN(FORBIDDEN, "관리자는 대상이 될 수 없습니다."), + FORBIDDEN_TEAMCALENDAR(FORBIDDEN, "팀 일정 접근 권한이 없습니다."), /** * 404 NOT_FOUND : Resource 를 찾을 수 없음 @@ -80,6 +84,9 @@ public enum ExceptionCode { MEMBERPHOTO_NOT_FOUND(NOT_FOUND, "등록된 프로필 사진이 없습니다."), LIKED_NOT_FOUND(NOT_FOUND, "등록된 공감이 없습니다."), CALENDAR_NOT_FOUND(NOT_FOUND, "등록된 일정 정보가 없습니다."), + TEAM_NOT_FOUND(NOT_FOUND, "등록된 팀이 없습니다."), + TEAMMEMBER_NOT_FOUND(NOT_FOUND, "동록되지 않은 팀 회원이 있습니다."), + TEAMCALENDAR_NOT_FOUND(NOT_FOUND, "등록된 팀 일정이 없습니다."), DELETED_POST(NOT_FOUND, "삭제된 게시글 입니다."), DELETED_COMMENT(NOT_FOUND, "삭제된 댓글 입니다."), @@ -101,13 +108,16 @@ public enum ExceptionCode { DUPLICATE_LIKED_MEMBER(CONFLICT, "이미 좋아요한 멤버 입니다."), DUPLICATE_DISLIKED_MEMBER(CONFLICT, "이미 싫어요한 멤버 입니다."), DUPLICATE_LIKED(CONFLICT, "공감은 한 번만 가능합니다."), + DUPLICATE_TEAM(CONFLICT, "등록된 팀이 존재합니다."), CANNOT_UPDATE_NICKNAME(CONFLICT, "닉네임은 30일마다 변경할 수 있습니다."), CANNOT_LIKED_SELF(CONFLICT, "본인의 글에 공감할 수 없습니다."), CANNOT_LIKED_POST_BY_DELETED_MEMBER(CONFLICT, "게시글 작성자가 탈퇴한 글은 공감할 수 없습니다."), + CANNOT_EXPLODE_TEAM(CONFLICT, "팀원이 존재하므로 팀을 삭제할 수 없습니다."), MEMBER_CANNOT_SELFREPORT(CONFLICT, "본인은 신고할 수 없습니다."), POST_CANNOT_SELFREPORT(CONFLICT, "본인의 게시글은 신고할 수 없습니다."), COMMENT_CANNOT_SELFREPORT(CONFLICT, "본인의 댓글은 신고할 수 없습니다."), + TEAM_STATUS_FULL(CONFLICT, "등록하려는 팀이 이미 만석입니다."), /** * 413 PAYLOAD_TOO_LARGE diff --git a/src/main/java/idorm/idormServer/member/domain/Member.java b/src/main/java/idorm/idormServer/member/domain/Member.java index 22bae531..6397dfd6 100644 --- a/src/main/java/idorm/idormServer/member/domain/Member.java +++ b/src/main/java/idorm/idormServer/member/domain/Member.java @@ -1,11 +1,11 @@ package idorm.idormServer.member.domain; +import idorm.idormServer.calendar.domain.Team; import idorm.idormServer.common.BaseEntity; import idorm.idormServer.community.domain.Comment; import idorm.idormServer.community.domain.Post; import idorm.idormServer.community.domain.PostLikedMember; import idorm.idormServer.email.domain.Email; -import idorm.idormServer.matchingInfo.domain.DormCategory; import idorm.idormServer.matchingInfo.domain.MatchingInfo; import idorm.idormServer.photo.domain.MemberPhoto; import lombok.*; @@ -35,6 +35,7 @@ public class Member extends BaseEntity implements UserDetails { private LocalDate fcmTokenUpdatedAt; private Character dormCategory; private Integer reportedCount; + private Integer teamOrder; @OneToMany(mappedBy = "member") private List emails = new ArrayList<>(); @@ -66,6 +67,10 @@ public class Member extends BaseEntity implements UserDetails { @OneToMany(mappedBy = "member") private List comments = new ArrayList<>(); + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "team_id") + private Team team; + @ElementCollection(fetch = FetchType.EAGER) private Set roles = new HashSet<>(); @@ -77,6 +82,8 @@ public Member(Email email, String password, String nickname) { this.nicknameUpdatedAt = null; this.reportedCount = 0; this.dormCategory = null; + this.team = null; + this.teamOrder = -999; this.roles.add("ROLE_USER"); this.setIsDeleted(false); @@ -119,9 +126,7 @@ public MemberPhoto getMemberPhoto() { return memberPhoto; } - /** - * 회원 탈퇴 시 사용 - */ + // 회원 탈퇴 시 사용 public List getAllMemberPhoto() { return this.memberPhotos; } @@ -165,6 +170,23 @@ public void deleteFcmToken() { this.fcmTokenUpdatedAt = LocalDate.now(); } + public void updateTeam(Team team, Integer teamOrder) { + this.team = team; + this.teamOrder = teamOrder; + } + + public void updateTeamOrder(Team team, Integer teamOrder) { + if (this.team.equals(team)) + this.teamOrder = teamOrder; + } + + public void deleteTeam(Team team) { + if (this.team.getId().equals(team.getId())) { + this.team = null; + this.teamOrder = -999; + } + } + public void incrementreportedCount() { this.reportedCount += 1; } diff --git a/src/main/java/idorm/idormServer/member/service/MemberService.java b/src/main/java/idorm/idormServer/member/service/MemberService.java index e3bdcdba..12075b5a 100644 --- a/src/main/java/idorm/idormServer/member/service/MemberService.java +++ b/src/main/java/idorm/idormServer/member/service/MemberService.java @@ -14,6 +14,7 @@ import java.time.LocalDateTime; import java.util.List; +import java.util.Optional; import static idorm.idormServer.exception.ExceptionCode.*; @@ -114,6 +115,18 @@ public Member findById(Long memberId) { .orElseThrow(() -> new CustomException(null, MEMBER_NOT_FOUND)); } + /** + * 회원 단건 조회 Optional | + * 500(SERVER_ERROR) + */ + public Optional findByIdOptional(Long memberId) { + try { + return memberRepository.findByIdAndIsDeletedIsFalse(memberId); + } catch (RuntimeException e) { + throw new CustomException(e, SERVER_ERROR); + } + } + /** * 이메일로 회원 단건 조회 | * 404(MEMBER_NOT_FOUND) @@ -209,4 +222,22 @@ public void validateUpdateNicknameIsChanged(Member member, String newNickname) { if (member.getNickname().equals(newNickname)) throw new CustomException(null, DUPLICATE_SAME_NICKNAME); } + + /** + * 관리자 대상 여부 검증 | + * 403(FORBIDDEN_TARGET_ADMIN) + */ + public void validateTargetAdmin(Member member) { + if(member.getRoles().contains("ROLE_ADMIN")) + throw new CustomException(null, FORBIDDEN_TARGET_ADMIN); + } + + /** + * 본인 대상 여부 검증 | + * 400(ILLEGAL_ARGUMENT_SELF) + */ + public void validateTargetSelf(Member loginMember, Member targetMember) { + if (loginMember.equals(targetMember)) + throw new CustomException(null,ILLEGAL_ARGUMENT_SELF); + } }