diff --git a/.github/workflows/DBDOCS_BUILD.yml b/.github/workflows/DBDOCS_BUILD.yml new file mode 100644 index 00000000..f4942f8f --- /dev/null +++ b/.github/workflows/DBDOCS_BUILD.yml @@ -0,0 +1,27 @@ +name: DBDOCS_BUILD + +on: + push: + branches: [ "dev" ] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + # DBDocs Install + - name: Install dbdocs + run: sudo npm install -g dbdocs + + #DBDocs Install Check + - name: Check dbdocs + run: dbdocs + + # Runs a set of commands using the runners shell + - name: Update dbdocs project + + env: + DBDOCS_TOKEN: ${{ secrets.DBDOCS_TOKEN }} + run: dbdocs build ./Docs/Picasso.dbml --project=Picasso \ No newline at end of file diff --git a/Api/src/main/java/picasso/server/api/test/HomeController.java b/Api/src/main/java/picasso/server/api/HomeController.java similarity index 65% rename from Api/src/main/java/picasso/server/api/test/HomeController.java rename to Api/src/main/java/picasso/server/api/HomeController.java index 94766969..01284ec3 100644 --- a/Api/src/main/java/picasso/server/api/test/HomeController.java +++ b/Api/src/main/java/picasso/server/api/HomeController.java @@ -1,18 +1,15 @@ -package picasso.server.api.test; +package picasso.server.api; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; +@Slf4j @Controller public class HomeController { @GetMapping("/") public String index() { return "/index"; } - - @GetMapping("/layout") - public String layout() { - return "/test_layout"; - } } diff --git a/Api/src/main/java/picasso/server/api/config/advice/GlobalControllerExceptionHandler.java b/Api/src/main/java/picasso/server/api/config/advice/GlobalControllerExceptionHandler.java new file mode 100644 index 00000000..c388d6ac --- /dev/null +++ b/Api/src/main/java/picasso/server/api/config/advice/GlobalControllerExceptionHandler.java @@ -0,0 +1,36 @@ +package picasso.server.api.config.advice; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import picasso.server.common.dto.ErrorDetail; +import picasso.server.common.exception.BaseException; +import picasso.server.common.exception.GlobalException; + +//TODO : 현재 `GlobalRestControllerExceptionHandler`와 동일한 코드, 추후 수정 필요 +@Slf4j +@RequiredArgsConstructor +@RestControllerAdvice +public class GlobalControllerExceptionHandler { + @ExceptionHandler(BaseException.class) + protected ResponseEntity baseExceptionHandle(BaseException e, HttpServletRequest request) { + ErrorDetail errorDetail = e.getErrorCode().getErrorDetail(); + log.warn("ExceptionName >>> {}, ErrorCode >>> {}, ExceptionReason >>> {}", + e.getClass(), errorDetail.getStatusCode(), errorDetail.getReason()); + + return ResponseEntity.status(errorDetail.getStatusCode()).body(errorDetail); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + protected ResponseEntity argumentNotValidHandle(MethodArgumentNotValidException e, HttpServletRequest request) { + ErrorDetail errorDetail = ErrorDetail.builder() + .statusCode(GlobalException.METHOD_ARGUMENT_ERROR.getStatusCode()) + .reason(GlobalException.METHOD_ARGUMENT_ERROR.getReason()) + .build(); + return ResponseEntity.status(errorDetail.getStatusCode()).body(errorDetail); + } +} diff --git a/Api/src/main/java/picasso/server/api/config/advice/GlobalRestControllerExceptionHandler.java b/Api/src/main/java/picasso/server/api/config/advice/GlobalRestControllerExceptionHandler.java new file mode 100644 index 00000000..84f0e4f8 --- /dev/null +++ b/Api/src/main/java/picasso/server/api/config/advice/GlobalRestControllerExceptionHandler.java @@ -0,0 +1,36 @@ +package picasso.server.api.config.advice; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import picasso.server.common.dto.ErrorDetail; +import picasso.server.common.exception.BaseException; +import picasso.server.common.exception.GlobalException; + +//TODO : 현재 `GlobalControllerExceptionHandler`와 동일한 코드, 추후 수정 필요 +@Slf4j +@RequiredArgsConstructor +@RestControllerAdvice +public class GlobalRestControllerExceptionHandler { + @ExceptionHandler(BaseException.class) + protected ResponseEntity baseExceptionHandle(BaseException e, HttpServletRequest request) { + ErrorDetail errorDetail = e.getErrorCode().getErrorDetail(); + log.warn("ExceptionName >>> {}, ErrorCode >>> {}, ExceptionReason >>> {}", + e.getClass(), errorDetail.getStatusCode(), errorDetail.getReason()); + + return ResponseEntity.status(errorDetail.getStatusCode()).body(errorDetail); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + protected ResponseEntity argumentNotValidHandle(MethodArgumentNotValidException e, HttpServletRequest request) { + ErrorDetail errorDetail = ErrorDetail.builder() + .statusCode(GlobalException.METHOD_ARGUMENT_ERROR.getStatusCode()) + .reason(GlobalException.METHOD_ARGUMENT_ERROR.getReason()) + .build(); + return ResponseEntity.status(errorDetail.getStatusCode()).body(errorDetail); + } +} diff --git a/Api/src/main/java/picasso/server/api/test/TestController.java b/Api/src/main/java/picasso/server/api/config/controller/HealthController.java similarity index 50% rename from Api/src/main/java/picasso/server/api/test/TestController.java rename to Api/src/main/java/picasso/server/api/config/controller/HealthController.java index 6f02ee20..47074c07 100644 --- a/Api/src/main/java/picasso/server/api/test/TestController.java +++ b/Api/src/main/java/picasso/server/api/config/controller/HealthController.java @@ -1,21 +1,18 @@ -package picasso.server.api.test; +package picasso.server.api.config.controller; - -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @Slf4j -@RequestMapping("/test") -@RequiredArgsConstructor @RestController -public class TestController { - private final TestMessage testMessage; +@RequestMapping("/health") +public class HealthController { @GetMapping - public String rtnMsg() { - return testMessage.toString(); + public ResponseEntity healthCheck() { + return ResponseEntity.ok().build(); } } diff --git a/Api/src/main/java/picasso/server/api/test/TestMessage.java b/Api/src/main/java/picasso/server/api/test/TestMessage.java deleted file mode 100644 index 82ea1739..00000000 --- a/Api/src/main/java/picasso/server/api/test/TestMessage.java +++ /dev/null @@ -1,12 +0,0 @@ -package picasso.server.api.test; - -import lombok.Data; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -@Data -@Component -public class TestMessage { - @Value("${test-message}") - private String message; -} diff --git a/Api/src/main/resources/static/css/style.css b/Api/src/main/resources/static/css/style.css new file mode 100644 index 00000000..2b9e5eca --- /dev/null +++ b/Api/src/main/resources/static/css/style.css @@ -0,0 +1,29 @@ +/* footer */ +footer { + border-top: 1px solid #eee; + padding-top: 12px; + padding-bottom: 10px; + background-color: #bdbdbd; +} +footer .title { + font-weight: 700; + font-size: 16px; + color: #242424; + padding-left: 10px; + margin-bottom: 0; +} +footer .footer-menu { + margin-top: 24px; + display: flex; + justify-content: space-between; +} +footer .footer-menu li a { + font-size: 12px; + color: #6f6f6f; +} +footer .copyright { + color: #6f6f6f; + font-size: 10px; + margin-top: 10px; + padding-left: 10px; +} \ No newline at end of file diff --git a/Api/src/main/resources/templates/fragment/config.html b/Api/src/main/resources/templates/fragment/config.html index 357294c1..3ee1599f 100644 --- a/Api/src/main/resources/templates/fragment/config.html +++ b/Api/src/main/resources/templates/fragment/config.html @@ -11,6 +11,8 @@ + + diff --git a/Common/src/main/java/picasso/server/common/.keep b/Common/src/main/java/picasso/server/common/.keep deleted file mode 100644 index e69de29b..00000000 diff --git a/Common/src/main/java/picasso/server/common/config/ConfigurationPropertiesConfig.java b/Common/src/main/java/picasso/server/common/config/ConfigurationPropertiesConfig.java new file mode 100644 index 00000000..738b2fde --- /dev/null +++ b/Common/src/main/java/picasso/server/common/config/ConfigurationPropertiesConfig.java @@ -0,0 +1,10 @@ +package picasso.server.common.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties({ +}) +public class ConfigurationPropertiesConfig { +} diff --git a/Common/src/main/java/picasso/server/common/dto/ErrorDetail.java b/Common/src/main/java/picasso/server/common/dto/ErrorDetail.java new file mode 100644 index 00000000..07c9dbc7 --- /dev/null +++ b/Common/src/main/java/picasso/server/common/dto/ErrorDetail.java @@ -0,0 +1,21 @@ +package picasso.server.common.dto; + +import lombok.Builder; +import lombok.Getter; + +/** + * StatusCode : ControllerAdvice, RestControllerAdvice에서 ResponseEntity의 Status를 찾기 위함. + * Reason : ResponseEntity에서 내용반환에 필요한 컬럼 + */ +@Getter +@Builder +public class ErrorDetail { + private final Integer statusCode; + private final String reason; + public static ErrorDetail of(Integer statusCode, String reason ) { + return ErrorDetail.builder() + .statusCode(statusCode) + .reason(reason) + .build(); + } +} diff --git a/Common/src/main/java/picasso/server/common/exception/BaseErrorCode.java b/Common/src/main/java/picasso/server/common/exception/BaseErrorCode.java new file mode 100644 index 00000000..56258c10 --- /dev/null +++ b/Common/src/main/java/picasso/server/common/exception/BaseErrorCode.java @@ -0,0 +1,7 @@ +package picasso.server.common.exception; + +import picasso.server.common.dto.ErrorDetail; + +public interface BaseErrorCode { + ErrorDetail getErrorDetail(); +} diff --git a/Common/src/main/java/picasso/server/common/exception/BaseException.java b/Common/src/main/java/picasso/server/common/exception/BaseException.java new file mode 100644 index 00000000..0b4dbc96 --- /dev/null +++ b/Common/src/main/java/picasso/server/common/exception/BaseException.java @@ -0,0 +1,15 @@ +package picasso.server.common.exception; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import picasso.server.common.dto.ErrorDetail; + +@Getter +@AllArgsConstructor +public abstract class BaseException extends RuntimeException { + private BaseErrorCode errorCode; + + public ErrorDetail getErrorDetail() { + return this.errorCode.getErrorDetail(); + } +} diff --git a/Common/src/main/java/picasso/server/common/exception/ExampleException.java b/Common/src/main/java/picasso/server/common/exception/ExampleException.java new file mode 100644 index 00000000..afc46264 --- /dev/null +++ b/Common/src/main/java/picasso/server/common/exception/ExampleException.java @@ -0,0 +1,12 @@ +package picasso.server.common.exception; + +public class ExampleException extends BaseException { + + public static final BaseException EXCEPTION = new ExampleException(); + + private ExampleException() { + super(GlobalException.EXAMPLE_ERROR); + } + +} + diff --git a/Common/src/main/java/picasso/server/common/exception/GlobalException.java b/Common/src/main/java/picasso/server/common/exception/GlobalException.java new file mode 100644 index 00000000..2637300d --- /dev/null +++ b/Common/src/main/java/picasso/server/common/exception/GlobalException.java @@ -0,0 +1,26 @@ +package picasso.server.common.exception; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import picasso.server.common.dto.ErrorDetail; + +@Getter +@AllArgsConstructor +public enum GlobalException implements BaseErrorCode { + EXAMPLE_ERROR(BAD_REQUEST.value(), "에러 예시 입니다."), + METHOD_ARGUMENT_ERROR(BAD_REQUEST.value(), "메서드 인자가 유효하지 않거나 @Valid를 통과하지 못하여 발생하는 예외입니다."), + INTERNAL_SERVER_ERRORS(INTERNAL_SERVER_ERROR.value(), "서버 내부 오류입니다."), + DATE_FORMAT_ERROR(BAD_REQUEST.value(), "날짜 형식을 확인해주세요."), + ; + + private final Integer statusCode; + private final String reason; + + @Override + public ErrorDetail getErrorDetail() { + return ErrorDetail.of(statusCode, reason); + } +} diff --git a/Docs/Picasso.dbml b/Docs/Picasso.dbml new file mode 100644 index 00000000..20b1e321 --- /dev/null +++ b/Docs/Picasso.dbml @@ -0,0 +1,120 @@ +Project Picasso{ + database_type: 'MySQL' + Note: ''' + # Picasso DB스키마 구조 + + ## 전달사항 + - 완벽한 ERD는 아니에요 수정필요하면 맘껏 수정하셔도 됩니다. + ''' +} + +Table Auth.user [ + headercolor: #E4A62E, note: '사용자 계정 정보 저장 테이블' +] { + id long [pk, increment, note: "User UID"] + email varchar [unique, not null , note: "사용자 로그인 ID"] + password varchar [not null, note: "사용자 패스워드"] + nick_name varchar [unique, not null, note: "사용자 닉네임"] + profile varchar [note: "프로필 링크"] + point long [default: 0, note: "사용자 보유중인 포인트 금액"] + login_type varchar [ + not null, default: "DEFAULT", + note: ''' + 사용자 회원가입 종류 + - DEFAULT: 기본 사용자 가입 + - KAKAO: 카카오 + - NAVER: 네이버 + ''' + ] + status varchar [ + not null, + note: ''' + 가입 방법에 따라 DEFAULT가 달라져야 하기 때문에 DEFAULT를 일부로 부여하지 않음. + - NOT_ACTIVE: 계정 인증 전 + - ACTIVE: 계정 인증 및 정상 사용 가능 + - SUSPENSION: 계정 정지 + - DELETE: 회원 탈퇴 및 삭제 필요 계정 + ''' + ] + role varchar [ + not null, default: "USER", + note: ''' + 사용자 권한 + - USER: 사용자 + - ADMIN: 관리자 + ''' + ] + created_at datetime [not null, default: `now()`, note: '회원 가입일'] + updated_at datetime [not null, default: `now()`, note: '마지막 업데이트일'] +} + +Table Auth.user_login_history [ + headercolor: #E4A62E, note: '사용자 계정 로그인 기록 로그' +] { + id long [pk, increment, note: "pk"] + user_id long [ref: > Auth.user.id, note: '사용자 UID'] + login_at datetime [not null, note: '사용자 로그인 일시'] +} + +Table Auth.social_info [ + headercolor: #E4A62E, + note: '소셜 사용자를 위한 추가 정보, 추후 소셜로그인 구현시 사용' +]{ + user_id long [ref: - Auth.user.id] + access_token varchar [not null, note: '액세스 토큰 [필요 없을수도 있슴]'] + refresh_token varchar [not null, note: '소셜 재로그인을 위한 리프래시 토큰'] + created_at datetime [not null, default: `now()`] +} + +//--------------------------------------------------------- +Table Board.article[ + headercolor: #24BAB1, + note: '게시물(경매 물품 게시물)' +] { + id long [pk, increment, note: '게시물 ID'] + title varchar [not null, note: '작품명, 게시물 제목'] + content blob [note: '게시물 내용, 물품에 대한 상세 설명'] + photo_link varchar [not null, note: '게시물 이미지 링크'] + artist varchar [not null, note: "화가 명"] + min_increase_amount long [not null, note: "최소 입찰 단위"] + start_amount long [not null, default: 0, note: "입찰 시작가"] + now_amount long [not null, note: "현재 입찰가(최고가)"] + status varchar [default: 'BEFORE_APPROVE', note:''' + 게시물 상태 + - BEFORE_APPROVE : 입찰 전(관리자 승인 전) + - AFTER_APPROVE : 입찰 전(관리자 승인 후) + - BIDDING : 입찰중 + - REJECT : 경매종료(유찰) + - SUCCESS_BID : 경매종료(낙찰) + + 게시물의 경우 BeforeApprove 상태에서만 수정 가능 + '''] + created_at datetime [default: `now()`, note: '게시물 생성일'] + created_user long [not null, note: '게시물 작성 사용자 ID입력, 관계를 묶지는 않은 이유는 Optional한 관계로 하기 떄문'] + updated_at datetime [default: `now()`, note: '게시물 정보 업데이트'] + updated_user long [not null, note: '마지막에 수정한 사용자 ID입력, 관계를 묶지는 않은 이유는 Optional한 관계로 하기 떄문'] +} + +Table Board.article_amount_history [ + headercolor: #24BAB1, + note: '금액 갱신 히스토리' +] { + id long [pk, increment, note: 'PK를 위한 ID'] + article_id long [ref: > Board.article.id, note:'게시물 ID'] + amount long [note: '최고가를 갱신했던 금액들'] + created_at datetime [default: 'now()', note: '해당 금액 갱신 시간'] +} + + +//사용자 정보 관련 그룹 +TableGroup User { + Auth.user + Auth.social_info + Auth.user_login_history +} + +// 게시물 그룹 +TableGroup Board_Article { + Board.article + Board.article_amount_history +} diff --git a/README.md b/README.md index 9fc1b209..1efd92b7 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,12 @@ --- ## ERD +> [!NOTE] +> DBDocs의 문서화 내용 변경하고싶은 경우 +> `./Docs/Picasso.dbml`의 파일 내용을 변경한 다음 `dev`브랜치에 Push가 진행될 경우 자동 갱신됩니다. + +- [DBDocs 링크](https://dbdocs.io/donsonioc2010/Picasso) --- ## 구현 기능