Skip to content

Commit

Permalink
release: 1.0.1 배포
Browse files Browse the repository at this point in the history
  • Loading branch information
jung-woo-kim authored Jul 26, 2023
2 parents 55f0c41 + cf90a50 commit 3044fbe
Show file tree
Hide file tree
Showing 114 changed files with 3,006 additions and 814 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,6 @@ out/

### VS Code ###
.vscode/

### MAC DSStore
.DS_Store
44 changes: 44 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## 모각코하기 좋은 카페 지도 애플리케이션, 모카콩(MOCACONG)

코딩하기 좋은 카페만 모아놓은 지도는 없을까? 고민했다면 모카콩!

<p align="center">
<img width="20%" height="400" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/b8510091-a49a-439d-b067-eb92afe282ab">
<img width="20%" height="400" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/ed9d723f-8161-4de4-9dae-3cfc2eea43b1">
<img width="20%" height="400" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/614c6e44-9199-4042-9f29-b90f659dc613">
<img width="20%" height="400" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/a81d4fbd-a4a4-4ebb-bc63-c8e0e815e2cb">
</p>


- - -

### MVP 기능
<p align="center">
<img width="90%" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/328495c2-4332-45b5-b052-359c1ed4cbd2">
</p>

- - -

### 사용 기술스택
- `Language`: Java 11, JUnit 5
- `Framework`: Spring Boot 2.7.9
- `Database`: H2, Amazon RDS for MySQL, Amazon Elasticache for Redis
- `ORM`: JPA (Spring Data JPA)
- `Deploy`: Github Actions, Docker CI/CD
- `Logging`: Logback, AWS Cloudwatch, AWS Lambda, Slack API
- `API Docs`: SpringDoc Swagger 3
- `Performance Test`: nGrinder

- - -

### ERD
<p align="center">
<img width="90%" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/1def43a1-9271-4b1f-947a-0ff8ad07f77d">
</p>

- - -

### 서비스 아키텍처
<p align="center">
<img width="90%" alt="image" src="https://github.com/mocacong/Mocacong-Backend/assets/57135043/27e4efcd-1d2e-4aca-a7c2-d655dede951a">
</p>
6 changes: 6 additions & 0 deletions src/main/java/mocacong/server/config/BatchConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import lombok.RequiredArgsConstructor;
import mocacong.server.service.CafeService;
import mocacong.server.service.MemberService;
import mocacong.server.service.ReportService;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
Expand All @@ -20,4 +21,9 @@ public void deleteNotUsedImages() {
memberService.deleteNotUsedProfileImages();
cafeService.deleteNotUsedCafeImages();
}

@Scheduled(cron = "0 0 0 * * *", zone = "Asia/Seoul") // 매일 자정에 실행
public void activateInactivateMembers() {
memberService.setActiveAfter60days();
}
}
53 changes: 27 additions & 26 deletions src/main/java/mocacong/server/controller/CafeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,19 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.List;
import javax.validation.Valid;
import lombok.RequiredArgsConstructor;
import mocacong.server.dto.request.*;
import mocacong.server.dto.response.*;
import mocacong.server.security.auth.LoginUserEmail;
import mocacong.server.security.auth.LoginUserId;
import mocacong.server.service.CafeService;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import java.util.List;

@Tag(name = "Cafes", description = "카페")
@RestController
@RequiredArgsConstructor
Expand All @@ -34,64 +35,64 @@ public ResponseEntity<Void> cafeRegister(@RequestBody @Valid CafeRegisterRequest
@SecurityRequirement(name = "JWT")
@GetMapping("/{mapId}")
public ResponseEntity<FindCafeResponse> findCafeByMapId(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId
) {
FindCafeResponse response = cafeService.findCafeByMapId(email, mapId);
FindCafeResponse response = cafeService.findCafeByMapId(memberId, mapId);
return ResponseEntity.ok(response);
}

@Operation(summary = "특정 카페 미리보기 조회")
@SecurityRequirement(name = "JWT")
@GetMapping("/{mapId}/preview")
public ResponseEntity<PreviewCafeResponse> previewCafeByMapId(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId
) {
PreviewCafeResponse response = cafeService.previewCafeByMapId(email, mapId);
PreviewCafeResponse response = cafeService.previewCafeByMapId(memberId, mapId);
return ResponseEntity.ok(response);
}

@Operation(summary = "특정 카페 리뷰 작성")
@SecurityRequirement(name = "JWT")
@PostMapping("/{mapId}")
public ResponseEntity<CafeReviewResponse> saveCafeReview(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestBody CafeReviewRequest request
@RequestBody @Valid CafeReviewRequest request
) {
CafeReviewResponse response = cafeService.saveCafeReview(email, mapId, request);
CafeReviewResponse response = cafeService.saveCafeReview(memberId, mapId, request);
return ResponseEntity.ok(response);
}

@Operation(summary = "특정 카페 내가 작성한 리뷰 조회")
@SecurityRequirement(name = "JWT")
@GetMapping("/{mapId}/me")
public ResponseEntity<CafeMyReviewResponse> findMyCafeReview(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId
) {
CafeMyReviewResponse response = cafeService.findMyCafeReview(email, mapId);
CafeMyReviewResponse response = cafeService.findMyCafeReview(memberId, mapId);
return ResponseEntity.ok(response);
}

@Operation(summary = "특정 카페 리뷰 수정")
@SecurityRequirement(name = "JWT")
@PutMapping("/{mapId}")
public ResponseEntity<CafeReviewUpdateResponse> updateCafeReview(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestBody CafeReviewUpdateRequest request
@RequestBody @Valid CafeReviewUpdateRequest request
) {
CafeReviewUpdateResponse response = cafeService.updateCafeReview(email, mapId, request);
CafeReviewUpdateResponse response = cafeService.updateCafeReview(memberId, mapId, request);
return ResponseEntity.ok(response);
}

@Operation(summary = "StudyType별로 카페 조회")
@SecurityRequirement(name = "JWT")
@PostMapping(value = "/studytypes")
public ResponseEntity<CafeFilterStudyTypeResponse> getCafesByStudyType(
@RequestParam(required = false) String studytype,
@RequestParam String studytype,
@RequestBody CafeFilterStudyTypeRequest request
) {
CafeFilterStudyTypeResponse responseBody = cafeService.filterCafesByStudyType(studytype, request);
Expand All @@ -102,48 +103,48 @@ public ResponseEntity<CafeFilterStudyTypeResponse> getCafesByStudyType(
@SecurityRequirement(name = "JWT")
@PostMapping("/favorites")
public ResponseEntity<CafeFilterFavoritesResponse> getCafesByFavorites(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@RequestBody CafeFilterFavoritesRequest request
) {
CafeFilterFavoritesResponse responseBody = cafeService.filterCafesByFavorites(email, request);
CafeFilterFavoritesResponse responseBody = cafeService.filterCafesByFavorites(memberId, request);
return ResponseEntity.ok(responseBody);
}

@Operation(summary = "카페 이미지 업로드")
@SecurityRequirement(name = "JWT")
@PostMapping(value = "/{mapId}/img", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> saveCafeImage(
@LoginUserEmail String email,
public ResponseEntity<CafeImagesSaveResponse> saveCafeImage(
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestParam(value = "files") List<MultipartFile> multipartFiles
) {
cafeService.saveCafeImage(email, mapId, multipartFiles);
return ResponseEntity.ok().build();
CafeImagesSaveResponse response = cafeService.saveCafeImage(memberId, mapId, multipartFiles);
return ResponseEntity.ok(response);
}

@Operation(summary = "카페 이미지 조회")
@SecurityRequirement(name = "JWT")
@GetMapping("/{mapId}/img")
public ResponseEntity<CafeImagesResponse> getCafeImages(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestParam("page") final Integer page,
@RequestParam("count") final int count
) {
CafeImagesResponse response = cafeService.findCafeImages(email, mapId, page, count);
CafeImagesResponse response = cafeService.findCafeImages(memberId, mapId, page, count);
return ResponseEntity.ok(response);
}

@Operation(summary = "카페 이미지 수정")
@SecurityRequirement(name = "JWT")
@PutMapping(value = "/{mapId}/img/{cafeImageId}", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Void> updateCafeImage(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@PathVariable Long cafeImageId,
@RequestParam(value = "file") MultipartFile multipartFile
) {
cafeService.updateCafeImage(email, mapId, cafeImageId, multipartFile);
cafeService.updateCafeImage(memberId, mapId, cafeImageId, multipartFile);
return ResponseEntity.ok().build();
}
}
28 changes: 15 additions & 13 deletions src/main/java/mocacong/server/controller/CommentController.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
import mocacong.server.dto.request.CommentUpdateRequest;
import mocacong.server.dto.response.CommentSaveResponse;
import mocacong.server.dto.response.CommentsResponse;
import mocacong.server.security.auth.LoginUserEmail;
import mocacong.server.security.auth.LoginUserId;
import mocacong.server.service.CommentService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;

@Tag(name = "Comments", description = "댓글")
@RestController
@RequiredArgsConstructor
Expand All @@ -25,62 +27,62 @@ public class CommentController {
@SecurityRequirement(name = "JWT")
@PostMapping
public ResponseEntity<CommentSaveResponse> saveComment(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestBody CommentSaveRequest request
@RequestBody @Valid CommentSaveRequest request
) {
CommentSaveResponse response = commentService.save(email, mapId, request.getContent());
CommentSaveResponse response = commentService.save(memberId, mapId, request.getContent());
return ResponseEntity.ok(response);
}

@Operation(summary = "카페 코멘트 조회")
@SecurityRequirement(name = "JWT")
@GetMapping
public ResponseEntity<CommentsResponse> findComments(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestParam("page") final Integer page,
@RequestParam("count") final int count
) {
CommentsResponse response = commentService.findAll(email, mapId, page, count);
CommentsResponse response = commentService.findAll(memberId, mapId, page, count);
return ResponseEntity.ok(response);
}

@Operation(summary = "카페 코멘트 중 나의 코멘트만 조회")
@SecurityRequirement(name = "JWT")
@GetMapping("/me")
public ResponseEntity<CommentsResponse> findCommentsByMe(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@RequestParam("page") final Integer page,
@RequestParam("count") final int count
) {
CommentsResponse response = commentService.findCafeCommentsOnlyMyComments(email, mapId, page, count);
CommentsResponse response = commentService.findCafeCommentsOnlyMyComments(memberId, mapId, page, count);
return ResponseEntity.ok(response);
}

@Operation(summary = "카페 코멘트 수정")
@SecurityRequirement(name = "JWT")
@PutMapping("/{commentId}")
public ResponseEntity<Void> updateComment(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@PathVariable Long commentId,
@RequestBody CommentUpdateRequest request
@RequestBody @Valid CommentUpdateRequest request
) {
commentService.update(email, mapId, request.getContent(), commentId);
commentService.update(memberId, mapId, request.getContent(), commentId);
return ResponseEntity.ok().build();
}

@Operation(summary = "카페 코멘트 삭제")
@SecurityRequirement(name = "JWT")
@DeleteMapping("/{commentId}")
public ResponseEntity<Void> deleteComment(
@LoginUserEmail String email,
@LoginUserId Long memberId,
@PathVariable String mapId,
@PathVariable Long commentId
) {
commentService.delete(email, mapId, commentId);
commentService.delete(memberId, mapId, commentId);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package mocacong.server.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import mocacong.server.dto.response.CommentLikeSaveResponse;
import mocacong.server.security.auth.LoginUserId;
import mocacong.server.service.CommentLikeService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "CommentsLike", description = "댓글 좋아요")
@RestController
@RequiredArgsConstructor
@RequestMapping("/comments/{commentId}/like")
public class CommentLikeController {
private final CommentLikeService commentLikeService;

@Operation(summary = "댓글 좋아요 등록")
@SecurityRequirement(name = "JWT")
@PostMapping
public ResponseEntity<CommentLikeSaveResponse> saveCommentLike(
@LoginUserId Long memberId,
@PathVariable Long commentId
) {
CommentLikeSaveResponse response = commentLikeService.save(memberId, commentId);
return ResponseEntity.ok(response);
}

@Operation(summary = "댓글 좋아요 삭제")
@SecurityRequirement(name = "JWT")
@DeleteMapping
public ResponseEntity<Void> deleteCommentLike(
@LoginUserId Long memberId,
@PathVariable Long commentId
) {
commentLikeService.delete(memberId, commentId);
return ResponseEntity.ok().build();
}
}
Loading

0 comments on commit 3044fbe

Please sign in to comment.