Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: feature
assignees: ''

---

## 어떤 기능인가요?

> 추가하려는 기능에 대해 간결하게 설명해주세요

## 작업 상세 내용

- [ ] TODO
- [ ] TODO
- [ ] TODO

## 참고할만한 자료(선택)
21 changes: 21 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
## 관련 이슈

- resolves: #이슈 번호

## 작업 내용

<!-- 해당 PR에서 작업한 내용을 간략히 설명해 주세요. (이미지 첨부 가능) -->

<!-- 코드가 아닌 기능 단위로 설명을 작성하며, 기능이 여러 개인 경우 각각을 잘 구분하여 설명해 주세요. -->

## 특이 사항

<!-- 프로젝트 실행에 영향을 미치는 중요한 변경사항이나 주의사항 등을 기술해 주세요. -->

## 리뷰 요구사항 (선택)

<!-- 리뷰 중점 사항: 리뷰어가 특히 집중해서 봐야 할 부분이 있나요? -->

<!-- 추가 검토 사항: 코드, 디자인, 구현 방식 등에 대한 추가적인 검토가 필요한 사항이 있나요? -->

<!-- 논의가 필요한 부분: 코드 리뷰 중 논의가 필요해 보이는 부분은 무엇인가요? -->
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,8 @@ out/

### YML ###
application-secret.yml
application-prod.yml
application-prod.yml

### docker volumes ###
mysql_data_local
redis_data_local
3 changes: 2 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ dependencies {//todo: 안쓰는 의존성이나 deprecated된 의존성 제거
implementation 'org.hibernate.validator:hibernate-validator:8.0.1.Final'
implementation 'jakarta.annotation:jakarta.annotation-api:2.1.1'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
testImplementation "org.mockito:mockito-core:3.3.3"

compileOnly 'org.projectlombok:lombok:1.18.26'
annotationProcessor 'org.projectlombok:lombok'
Expand All @@ -62,4 +63,4 @@ sourceSets {

compileJava {
options.annotationProcessorGeneratedSourcesDirectory = file('build/generated/sources/annotationProcessor/java/main')
}
}
14 changes: 4 additions & 10 deletions docker-compose.local.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '3.8'

services:
mysql:
image: mysql:8.0
Expand All @@ -11,17 +9,13 @@ services:
MYSQL_PASSWORD: solid_connection_local_password
ports:
- "3306:3306"
# volumes:
# - mysql_data_local:/var/lib/mysql
volumes:
- ./mysql_data_local:/var/lib/mysql

redis:
image: redis:latest
container_name: solid-connection-local-redis
ports:
- "6379:6379"
# volumes:
# - redis_data_local:/data

#volumes:
# mysql_data_local:
# redis_data_local:
volumes:
- ./redis_data_local:/data
12 changes: 12 additions & 0 deletions local_compose_down.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash

set -e

echo "Starting all docker containers..."
docker-compose -f docker-compose.local.yml down

echo "Pruning unused Docker images..."
docker image prune -f

echo "Containers are up and running."
docker-compose ps -a
23 changes: 23 additions & 0 deletions local_compose_up.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash

# 명령이 0이 아닌 종료값을 가질때 즉시 종료
set -e

if [ ! -d "mysql_data_local" ]; then
echo "mysql_data_local 디렉토리가 없습니다. 디렉토리를 생성합니다."
mkdir -p mysql_data_local
fi

if [ ! -d "redis_data_local" ]; then
echo "redis_data_local 디렉토리가 없습니다. 디렉토리를 생성합니다."
mkdir -p redis_data_local
fi

echo "Starting all docker containers..."
docker-compose -f docker-compose.local.yml up -d

echo "Pruning unused Docker images..."
docker image prune -f

echo "Containers are up and running."
docker-compose ps -a
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@EnableJpaAuditing
@SpringBootApplication
public class SolidConnectionApplication {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class ApplicationQueryService {
* 다른 지원자들의 성적을 조회한다.
* - 유저가 다른 지원자들을 볼 수 있는지 검증한다.
* - 지역과 키워드를 통해 대학을 필터링한다.
* - 지역은 영어 대문자로 받는다 e.g. ASIA
* - 1지망, 2지망 지원자들을 조회한다.
* */
@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@ public record SignUpRequest(
@Schema(description = "카카오 인증 토큰", example = "kakaoToken123")
String kakaoOauthToken,

@ArraySchema(schema = @Schema(description = "관심 지역 목록", example = "[\"아시아\", \"유럽\"]"))
@ArraySchema(schema = @Schema(description = "관심 지역 목록", example = "아시아권"))
List<String> interestedRegions,

@ArraySchema(schema = @Schema(description = "관심 국가 목록", example = "[\"일본\", \"독일\"]"))
@ArraySchema(schema = @Schema(description = "관심 국가 목록", example = "일본"))
List<String> interestedCountries,

@Schema(description = "지원 준비 단계", example = "CONSIDERING")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.example.solidconnection.board.controller;

import com.example.solidconnection.board.service.BoardService;
import com.example.solidconnection.post.dto.BoardFindPostResponse;
import com.example.solidconnection.type.BoardCode;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityRequirements;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

import static com.example.solidconnection.config.swagger.SwaggerConfig.ACCESS_TOKEN;

@RestController
@RequiredArgsConstructor
@RequestMapping("/communities")
@SecurityRequirements
@SecurityRequirement(name = ACCESS_TOKEN)
public class BoardController {

private final BoardService boardService;

// todo: 회원별로 접근 가능한 게시판 목록 조회 기능 개발
@GetMapping()
public ResponseEntity<?> findAccessibleCodes() {
List<String> accessibleCodeList = new ArrayList<>();
for (BoardCode boardCode : BoardCode.values()) {
accessibleCodeList.add(String.valueOf(boardCode));
}
return ResponseEntity.ok().body(accessibleCodeList);
}

@GetMapping("/{code}")
public ResponseEntity<?> findPostsByCodeAndCategory(
@PathVariable(value = "code") String code,
@RequestParam(value = "category", defaultValue = "전체") String category) {

List<BoardFindPostResponse> postsByCodeAndPostCategory = boardService
.findPostsByCodeAndPostCategory(code, category);
return ResponseEntity.ok().body(postsByCodeAndPostCategory);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.example.solidconnection.board.domain;

import com.example.solidconnection.post.domain.Post;
import jakarta.persistence.*;
import lombok.*;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@NoArgsConstructor
public class Board {

@Id
@Column(length = 20)
private String code;

@Column(nullable = false, length = 20)
private String koreanName;

@OneToMany(mappedBy = "board", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Post> postList = new ArrayList<>();

public Board(String code, String koreanName) {
this.code = code;
this.koreanName = koreanName;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.example.solidconnection.board.dto;

import com.example.solidconnection.board.domain.Board;

public record PostFindBoardResponse(
String code,
String koreanName
) {
public static PostFindBoardResponse from(Board board) {
return new PostFindBoardResponse(
board.getCode(),
board.getKoreanName()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.solidconnection.board.repository;

import com.example.solidconnection.board.domain.Board;
import com.example.solidconnection.custom.exception.CustomException;
import com.example.solidconnection.custom.exception.ErrorCode;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import java.util.Optional;

import static com.example.solidconnection.custom.exception.ErrorCode.INVALID_BOARD_CODE;

@Repository
public interface BoardRepository extends JpaRepository<Board, String> {

@EntityGraph(attributePaths = {"postList"})
Optional<Board> findBoardByCode(@Param("code") String code);

default Board getByCodeUsingEntityGraph(String code) {
return findBoardByCode(code)
.orElseThrow(() -> new CustomException(ErrorCode.INVALID_BOARD_CODE));
}

default Board getByCode(String code) {
return findById(code)
.orElseThrow(() -> new CustomException(INVALID_BOARD_CODE));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.example.solidconnection.board.service;

import com.example.solidconnection.board.domain.Board;
import com.example.solidconnection.board.repository.BoardRepository;
import com.example.solidconnection.custom.exception.CustomException;
import com.example.solidconnection.custom.exception.ErrorCode;
import com.example.solidconnection.post.domain.Post;
import com.example.solidconnection.post.dto.BoardFindPostResponse;
import com.example.solidconnection.type.BoardCode;
import com.example.solidconnection.type.PostCategory;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class BoardService {
private final BoardRepository boardRepository;

private String validateCode(String code) {
try {
return String.valueOf(BoardCode.valueOf(code));
} catch (IllegalArgumentException ex) {
throw new CustomException(ErrorCode.INVALID_BOARD_CODE);
}
}

private PostCategory validatePostCategory(String postCategory) {
try {
return PostCategory.valueOf(postCategory);
} catch (IllegalArgumentException ex) {
throw new CustomException(ErrorCode.INVALID_POST_CATEGORY);
}
}

@Transactional(readOnly = true)
public List<BoardFindPostResponse> findPostsByCodeAndPostCategory(String code, String category) {

String boardCode = validateCode(code);
PostCategory postCategory = validatePostCategory(category);

Board board = boardRepository.getByCodeUsingEntityGraph(boardCode);
List<Post> postList = getPostListByPostCategory(board.getPostList(), postCategory);

return BoardFindPostResponse.from(postList);
}

private List<Post> getPostListByPostCategory(List<Post> postList, PostCategory postCategory) {
if (postCategory.equals(PostCategory.전체)) {
return postList;
}
return postList.stream()
.filter(post -> post.getCategory().equals(postCategory))
.collect(Collectors.toList());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.example.solidconnection.comment.dto;

import com.example.solidconnection.entity.Comment;
import com.example.solidconnection.siteuser.dto.PostFindSiteUserResponse;

import java.time.LocalDateTime;

public record PostFindCommentResponse(
Long id,
Long parentId,
String content,
Boolean isOwner,
LocalDateTime createdAt,
LocalDateTime updatedAt,
PostFindSiteUserResponse postFindSiteUserResponse

) {
public static PostFindCommentResponse from(Boolean isOwner, Comment comment) {
return new PostFindCommentResponse(
comment.getId(),
getParentCommentId(comment),
comment.getContent(),
isOwner,
comment.getCreatedAt(),
comment.getUpdatedAt(),
PostFindSiteUserResponse.from(comment.getSiteUser())
);
}

private static Long getParentCommentId(Comment comment) {
if (comment.getParentComment() != null) {
return comment.getParentComment().getId();
}
return null;
}
}
Loading