Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

merge develop to main #88

Merged
merged 73 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
c9e039a
feat: 공지사항 생성, 공지사항 읽기 기능 추가 (#1)
skfotakf Jul 25, 2023
f849a63
chore: .idea 디렉토리 삭제
leeeryboy Jul 26, 2023
54ef587
chore: PR 템플릿 생성 (#2)
leeeryboy Jul 23, 2023
3a92906
feat: 로컬 db용 docker-compose 파일 추가 및 application.yaml 수정 (#4)
leeeryboy Jul 26, 2023
c2f7ef8
feat: 공지사항 수정, 삭제, 태그 기능 추가 (#3)
skfotakf Jul 28, 2023
227a385
feat: 구성원(교수) 생성 및 조회 API 구현 (#8)
leeeryboy Jul 31, 2023
92a8e8b
Docs: Swagger 추가 (#7)
huGgW Jul 31, 2023
00566f2
feat: 페이지네이션+검색 기능 추가 (#5)
skfotakf Aug 1, 2023
38de652
CICD: 배포 자동화 (#6)
huGgW Aug 1, 2023
1a75adf
feat: 구성원(교수) 수정 및 삭제 API (#9)
leeeryboy Aug 5, 2023
42249ba
feat: 구성원(행정직원) CRUD API (#10)
leeeryboy Aug 5, 2023
41bb4fd
feat: news 패키지 추가, 디벨롭 및 프론트에 맞게 엔티티 변경 (#12)
skfotakf Aug 8, 2023
3bbccb9
Merge branch 'main' into develop
leeeryboy Aug 8, 2023
7e3ea8e
fix: main에서 develop으로 pr (#16)
skfotakf Aug 9, 2023
6c5afe2
feat: seminar 패키지 추가 (#17)
skfotakf Aug 11, 2023
a550011
hotfix: 불필요한 dto 삭제 (#20)
skfotakf Aug 11, 2023
e2b3dec
fix: 이미지 uri 필드 추가 및 프론트 요구사항 반영 (#21)
leeeryboy Aug 14, 2023
0813562
feat: introduction 패키지, undergraduate 패키지 추가 (#22)
skfotakf Aug 15, 2023
cffac41
feat: admissions, research 패키지 추가 (#23)
skfotakf Aug 15, 2023
21b7dd7
Merge branch 'main' into develop
leeeryboy Aug 16, 2023
d2c341c
feat: oidc 로그인 (#27)
leeeryboy Aug 22, 2023
e05b5ad
feat: cors 설정 (#30)
leeeryboy Aug 22, 2023
bf61a06
Merge branch 'main' into develop
leeeryboy Aug 22, 2023
28fa788
fix: cors 추가 설정 (#32)
leeeryboy Aug 22, 2023
3e0f8e7
fix: CORS (#34)
leeeryboy Aug 23, 2023
3fae6a4
Merge branch 'main' into develop
leeeryboy Aug 23, 2023
a86542c
fix: about, academics, admissions 패키지 수정 (#25)
skfotakf Aug 24, 2023
cc256bf
feat: 일반 예약 및 정기 예약 API (#28)
leeeryboy Aug 24, 2023
13161c9
feat: 예약 조회 API (#39)
leeeryboy Aug 28, 2023
191f6f6
feat: about, member, news, seminar 메인 이미지 업로드 추가 (#38)
skfotakf Aug 29, 2023
455ded8
CICD: Change deploy port to 8080 (#40)
huGgW Aug 29, 2023
f8862c8
Merge branch 'main' into develop
huGgW Aug 29, 2023
f629d47
[Merge] (#41) (#42)
leeeryboy Aug 29, 2023
7b78310
feat: 장학제도 GET API 및 장학제도 페이지 응답 수정 (#44)
leeeryboy Aug 30, 2023
233a8e6
feat: custom metadata 설정 + 리다이렉트 엔드포인트 변경 (#45)
leeeryboy Aug 30, 2023
b86741f
feat: attachments 패키지 추가, news와 seminar request에 attachments 추가 (#43)
skfotakf Aug 30, 2023
fb5193f
feat: Top Conference List GET API (#47)
leeeryboy Aug 30, 2023
d479520
feat: 파일 서빙, 다운로드, 삭제 API (#48)
leeeryboy Aug 31, 2023
3b5d67a
feat: 공지사항 글쓴이 추가 (#49)
leeeryboy Aug 31, 2023
b8fca73
CI/CD: Https 적용 및 백엔드, 데이터베이스, 프록시 서버 배포 분리 (#50)
huGgW Aug 31, 2023
8b38588
fix: 프론트랑 협의하여 내용 변경 + news, seminar에 image, attachments update 추가 (#51)
skfotakf Sep 1, 2023
3565704
fix: research 패키지 프론트에 맞춰 협의 (#52)
skfotakf Sep 2, 2023
79ca736
[Refactor] 로그인, file uri 배포 환경에 맞게 수정 (#53)
huGgW Sep 2, 2023
46cee1c
fix: https 적용 관련 로그인 수정 (#56)
leeeryboy Sep 2, 2023
6d8a2fc
fix: notice 패키지 프론트에 맞게 협의 (#54)
skfotakf Sep 2, 2023
068a38b
feat: 신임교수초빙 (#59)
leeeryboy Sep 2, 2023
d546a9f
feat: 행정실 권한 체크 API (#60)
leeeryboy Sep 2, 2023
6f23b0e
[CICD] mainImage, attachment 경로 mount 설정 (#55)
huGgW Sep 3, 2023
d806f6c
[Feat] 예전 url과 비슷하게 이전 파일(사진)들 접근할 수 있도록 controller 생성 (#58)
huGgW Sep 3, 2023
c0d71c0
Merge branch 'main' into develop
huGgW Sep 3, 2023
d99ac71
Merge branch 'main' into develop
huGgW Sep 3, 2023
433a2bf
fix: 예약 dto에 지도교수, 반복 횟수 추가 (#61)
leeeryboy Sep 3, 2023
878b4f4
fix: 로그아웃 엔드포인트 변경 (#64)
leeeryboy Sep 3, 2023
f79edc9
[Fix] Notice, News Description TEXT type으로 변환 (#65)
huGgW Sep 4, 2023
f47b845
fix: newsSearchResponse, seminarSearchResponse 수정 (#66)
skfotakf Sep 4, 2023
0c83efb
fix: 파일 업로드 경로 통일 (#68)
leeeryboy Sep 4, 2023
b1985bb
fix: 갯수만 fetch하도록 최적화 (#70)
leeeryboy Sep 5, 2023
46320ff
Fix: Change type of additionalNote column on SeminarEntity to "TEXT" …
huGgW Sep 5, 2023
a3e803f
fix: academics 패키지 프론트에 맞춰 협의, admin 패키지 추가 (#69)
skfotakf Sep 5, 2023
a3514fb
Refactor: HTML로부터 텍스트 추출 함수 Utils로 이동 (#73)
huGgW Sep 6, 2023
9f406af
Feat: Add plain text description for notice (#74)
huGgW Sep 7, 2023
0ce4199
fix: admissions 패키지 프론트에 맞게 협의 (#76)
skfotakf Sep 8, 2023
6876bd6
fix: 이전 글 다음 글만 fetch 하도록 쿼리 최적화 (#75)
leeeryboy Sep 8, 2023
b56ff8a
CI/CD: Test 설정 (#77)
huGgW Sep 8, 2023
b882c3a
[Test] Notice create, update 시 plainTextDescription 동작 테스트 (#78)
huGgW Sep 8, 2023
8d5627c
[Feat] Seminar plain text field 추가 (#79)
huGgW Sep 8, 2023
d2c0366
Feat: News plain text field 추가 (#80)
huGgW Sep 8, 2023
a409fa8
Feat: 구성원 검색 위한 table 추가. (#81)
huGgW Sep 8, 2023
b60f716
fix: 새소식 세미나 쿼리 수정 (#82)
leeeryboy Sep 9, 2023
6f72689
fix: main 프론트에 맞게 협의, notice 태그 enum 추가 (#83)
skfotakf Sep 10, 2023
cb9e3a1
fix: isPublic -> isPrivate으로 정리 (#86)
skfotakf Sep 11, 2023
cfc872e
Merge branch 'main' into develop
leeeryboy Sep 11, 2023
471b128
feat: 로컬 로그인 테스트용 엔드포인트로 임시 변경 (#87)
leeeryboy Sep 11, 2023
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
36 changes: 36 additions & 0 deletions .github/workflows/build_and_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
on:
pull_request:
branches:
- main
- develop

jobs:
build_and_test:
runs-on: ubuntu-latest

permissions:
contents: read
issues: read
checks: write
pull-requests: write

steps:
- uses: 'actions/checkout@v3'

- name: Setup Java JDK
uses: actions/setup-java@v3.12.0
with:
java-version: '17'
distribution: 'adopt'

- name: Build with Gradle
run: ./gradlew clean build -x test

- name: Run Tests with Gradle
run: ./gradlew test

- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v2.9.0
if: always()
with:
files: build/test-results/**/*.xml
21 changes: 15 additions & 6 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ dependencies {
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.springframework.security:spring-security-test")

// kotest
testImplementation("io.kotest:kotest-runner-junit5-jvm:5.7.1")
testImplementation("io.kotest:kotest-assertions-core-jvm:5.7.1")
testImplementation("io.kotest:kotest-property-jvm:5.7.1")
implementation("io.kotest.extensions:kotest-extensions-spring:1.1.3")

// mockk
testImplementation("io.mockk:mockk:1.13.7")
testImplementation("com.ninja-squad:springmockk:4.0.2")

// h2 database
implementation("org.springframework.boot:spring-boot-starter-jdbc")
testImplementation("com.h2database:h2")

//queryDsl
implementation("com.querydsl:querydsl-jpa:5.0.0:jakarta")
kapt("com.querydsl:querydsl-apt:5.0.0:jakarta")
Expand All @@ -51,12 +65,6 @@ dependencies {
// Custom Metadata
annotationProcessor("org.springframework.boot:spring-boot-configuration-processor")

// 이미지 업로드
implementation("commons-io:commons-io:2.11.0")

// 썸네일 보여주기
implementation("net.coobird:thumbnailator:0.4.19")

}
noArg {
annotation("jakarta.persistence.Entity")
Expand All @@ -79,4 +87,5 @@ tasks.withType<KotlinCompile> {

tasks.withType<Test> {
useJUnitPlatform()
systemProperty("spring.profiles.active", "test")
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class CustomAuthenticationSuccessHandler(
response: HttpServletResponse,
authentication: Authentication
) {
val redirectUrl = "${frontendEndpoint}/login/success"
val redirectUrl = "http://localhost:3000/login/success"
response.sendRedirect(redirectUrl)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class SecurityConfig(
response: HttpServletResponse?,
authentication: Authentication?
) {
val redirectUrl = "${endpointProperties.frontend}/logout/success"
val redirectUrl = "http://localhost:3000/logout/success"
super.setDefaultTargetUrl(redirectUrl)
super.onLogoutSuccess(request, response, authentication)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.wafflestudio.csereal.common.utils

import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Pageable
import kotlin.math.floor

class FixedPageRequest(pageable: Pageable, total: Long) :
PageRequest(getPageNum(pageable, total), pageable.pageSize, pageable.sort) {

companion object {
private fun getPageNum(pageable: Pageable, total: Long): Int {
val pageNum = pageable.pageNumber
val pageSize = pageable.pageSize
val requestCount = pageNum * pageSize

if (total > requestCount) {
return pageNum
}

return floor(total.toDouble() / pageSize).toInt()
}
}

}
10 changes: 10 additions & 0 deletions src/main/kotlin/com/wafflestudio/csereal/common/utils/Utils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.wafflestudio.csereal.common.utils

import org.jsoup.Jsoup
import org.jsoup.parser.Parser
import org.jsoup.safety.Safelist

fun cleanTextFromHtml(description: String): String {
val cleanDescription = Jsoup.clean(description, Safelist.none())
return Parser.unescapeEntities(cleanDescription, false)
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class AdminRepositoryImpl(
newsEntity.createdAt
)
).from(newsEntity)
.where(newsEntity.isDeleted.eq(false), newsEntity.isPublic.eq(true), newsEntity.isSlide.eq(true))
.where(newsEntity.isDeleted.eq(false), newsEntity.isPrivate.eq(false), newsEntity.isSlide.eq(true))
.orderBy(newsEntity.createdAt.desc())
.offset(40*pageNum)
.limit(40)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import org.springframework.web.bind.annotation.RestController
class AdmissionsController(
private val admissionsService: AdmissionsService
) {
@PostMapping("/undergraduate")
@PostMapping("/undergraduate/{postType}")
fun createUndergraduateAdmissions(
@RequestParam postType: String,
@PathVariable postType: String,
@Valid @RequestBody request: AdmissionsDto
) : AdmissionsDto {
return admissionsService.createUndergraduateAdmissions(postType, request)
Expand All @@ -32,9 +32,9 @@ class AdmissionsController(
return admissionsService.createGraduateAdmissions(request)
}

@GetMapping("/undergraduate")
@GetMapping("/undergraduate/{postType}")
fun readUndergraduateAdmissions(
@RequestParam postType: String
@PathVariable postType: String
) : ResponseEntity<AdmissionsDto> {
return ResponseEntity.ok(admissionsService.readUndergraduateAdmissions(postType))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import jakarta.persistence.Enumerated
class AdmissionsEntity(
@Enumerated(EnumType.STRING)
val postType: AdmissionsPostType,
val title: String,
val pageName: String,
val description: String,
): BaseTimeEntity() {
companion object {
fun of(postType: AdmissionsPostType, admissionsDto: AdmissionsDto) : AdmissionsEntity {
fun of(postType: AdmissionsPostType, pageName: String, admissionsDto: AdmissionsDto) : AdmissionsEntity {
return AdmissionsEntity(
postType = postType,
title = admissionsDto.title,
pageName = pageName,
description = admissionsDto.description,
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import java.time.LocalDateTime

data class AdmissionsDto(
val id: Long,
val title: String,
val description: String,
val createdAt: LocalDateTime?,
val modifiedAt: LocalDateTime?,
Expand All @@ -14,7 +13,6 @@ data class AdmissionsDto(
fun of(entity: AdmissionsEntity) : AdmissionsDto = entity.run {
AdmissionsDto(
id = this.id,
title = this.title,
description = this.description,
createdAt = this.createdAt,
modifiedAt = this.modifiedAt,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,13 @@ class AdmissionsServiceImpl(
override fun createUndergraduateAdmissions(postType: String, request: AdmissionsDto): AdmissionsDto {
val enumPostType = makeStringToAdmissionsPostType(postType)

val newAdmissions = AdmissionsEntity.of(enumPostType, request)
val pageName = when(enumPostType) {
AdmissionsPostType.UNDERGRADUATE_EARLY_ADMISSION -> "수시 모집"
AdmissionsPostType.UNDERGRADUATE_REGULAR_ADMISSION -> "정시 모집"
else -> throw CserealException.Csereal404("해당하는 페이지를 찾을 수 없습니다.")
}

val newAdmissions = AdmissionsEntity.of(enumPostType, pageName, request)

admissionsRepository.save(newAdmissions)

Expand All @@ -33,7 +39,7 @@ class AdmissionsServiceImpl(

@Transactional
override fun createGraduateAdmissions(request: AdmissionsDto): AdmissionsDto {
val newAdmissions: AdmissionsEntity = AdmissionsEntity.of(AdmissionsPostType.GRADUATE, request)
val newAdmissions: AdmissionsEntity = AdmissionsEntity.of(AdmissionsPostType.GRADUATE, "전기/후기 모집", request)

admissionsRepository.save(newAdmissions)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController

@RequestMapping
@RestController("/api/v1")
@RequestMapping("/api/v1")
@RestController
class MainController(
private val mainService: MainService,
) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,70 +1,121 @@
package com.wafflestudio.csereal.core.main.database

import com.querydsl.core.QueryFactory
import com.querydsl.core.types.Projections
import com.querydsl.jpa.impl.JPAQueryFactory
import com.sun.tools.javac.Main
import com.wafflestudio.csereal.core.main.dto.MainResponse
import com.wafflestudio.csereal.core.main.dto.NewsResponse
import com.wafflestudio.csereal.core.main.dto.NoticeResponse
import com.wafflestudio.csereal.core.main.dto.MainImportantResponse
import com.wafflestudio.csereal.core.main.dto.MainNoticeResponse
import com.wafflestudio.csereal.core.main.dto.MainSlideResponse
import com.wafflestudio.csereal.core.news.database.NewsRepository
import com.wafflestudio.csereal.core.news.database.QNewsEntity.newsEntity
import com.wafflestudio.csereal.core.notice.database.NoticeRepository
import com.wafflestudio.csereal.core.notice.database.QNoticeEntity.noticeEntity
import com.wafflestudio.csereal.core.notice.database.QNoticeTagEntity.noticeTagEntity
import com.wafflestudio.csereal.core.notice.database.QTagInNoticeEntity.tagInNoticeEntity
import com.wafflestudio.csereal.core.notice.database.TagInNoticeEnum
import com.wafflestudio.csereal.core.resource.mainImage.service.MainImageService
import com.wafflestudio.csereal.core.seminar.database.SeminarRepository

import org.springframework.stereotype.Component

interface MainRepository {
fun readMainSlide(): List<NewsResponse>
fun readMainNoticeTotal(): List<NoticeResponse>
fun readMainNoticeTag(tag: String): List<NoticeResponse>
fun readMainSlide(): List<MainSlideResponse>
fun readMainNoticeTotal(): List<MainNoticeResponse>
fun readMainNoticeTag(tagEnum: TagInNoticeEnum): List<MainNoticeResponse>
fun readMainImportant(): List<MainImportantResponse>
}

@Component
class MainRepositoryImpl(
private val queryFactory: JPAQueryFactory,
private val mainImageService: MainImageService,
private val noticeRepository: NoticeRepository,
private val newsRepository: NewsRepository,
private val seminarRepository: SeminarRepository,
) : MainRepository {
override fun readMainSlide(): List<NewsResponse> {
return queryFactory.select(
Projections.constructor(
NewsResponse::class.java,
newsEntity.id,
newsEntity.title,
newsEntity.createdAt
)
).from(newsEntity)
.where(newsEntity.isDeleted.eq(false), newsEntity.isPublic.eq(true), newsEntity.isSlide.eq(true))
override fun readMainSlide(): List<MainSlideResponse> {
val newsEntityList = queryFactory.selectFrom(newsEntity)
.where(newsEntity.isDeleted.eq(false), newsEntity.isPrivate.eq(false), newsEntity.isSlide.eq(true))
.orderBy(newsEntity.createdAt.desc())
.limit(20).fetch()

return newsEntityList.map {
val imageURL = mainImageService.createImageURL(it.mainImage)
MainSlideResponse(
id = it.id,
title = it.title,
imageURL = imageURL,
createdAt = it.createdAt
)
}
}

override fun readMainNoticeTotal(): List<NoticeResponse> {
override fun readMainNoticeTotal(): List<MainNoticeResponse> {
return queryFactory.select(
Projections.constructor(
NoticeResponse::class.java,
MainNoticeResponse::class.java,
noticeEntity.id,
noticeEntity.title,
noticeEntity.createdAt
)
).from(noticeEntity)
.where(noticeEntity.isDeleted.eq(false), noticeEntity.isPublic.eq(true))
.where(noticeEntity.isDeleted.eq(false), noticeEntity.isPrivate.eq(false))
.orderBy(noticeEntity.isPinned.desc()).orderBy(noticeEntity.createdAt.desc())
.limit(6).fetch()
}
override fun readMainNoticeTag(tag: String): List<NoticeResponse> {

override fun readMainNoticeTag(tagEnum: TagInNoticeEnum): List<MainNoticeResponse> {
return queryFactory.select(
Projections.constructor(
NoticeResponse::class.java,
MainNoticeResponse::class.java,
noticeTagEntity.notice.id,
noticeTagEntity.notice.title,
noticeTagEntity.notice.createdAt,
)
).from(noticeTagEntity)
.rightJoin(noticeEntity).on(noticeTagEntity.notice.eq(noticeEntity))
.rightJoin(tagInNoticeEntity).on(noticeTagEntity.tag.eq(tagInNoticeEntity))
.where(noticeTagEntity.tag.name.eq(tag))
.where(noticeEntity.isDeleted.eq(false), noticeEntity.isPublic.eq(true))
.where(noticeTagEntity.tag.name.eq(tagEnum))
.where(noticeEntity.isDeleted.eq(false), noticeEntity.isPrivate.eq(true))
.orderBy(noticeEntity.isPinned.desc()).orderBy(noticeEntity.createdAt.desc())
.limit(6).distinct().fetch()
}

override fun readMainImportant(): List<MainImportantResponse> {
val mainImportantResponses: MutableList<MainImportantResponse> = mutableListOf()
noticeRepository.findAllByIsImportant(true).forEach {
mainImportantResponses.add(
MainImportantResponse(
id = it.id,
title = it.title,
createdAt = it.createdAt,
category = "notice"
)
)
}

newsRepository.findAllByIsImportant(true).forEach {
mainImportantResponses.add(
MainImportantResponse(
id = it.id,
title = it.title,
createdAt = it.createdAt,
category = "news"
)
)
}

seminarRepository.findAllByIsImportant(true).forEach {
mainImportantResponses.add(
MainImportantResponse(
id = it.id,
title = it.title,
createdAt = it.createdAt,
category = "seminar"
)
)
}
mainImportantResponses.sortByDescending { it.createdAt }

return mainImportantResponses
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.wafflestudio.csereal.core.main.dto

import com.querydsl.core.annotations.QueryProjection
import java.time.LocalDateTime

data class NoticeResponse @QueryProjection constructor(
data class MainImportantResponse(
val id: Long,
val title: String,
val createdAt: LocalDateTime?,
){
val category: String,
) {
}
Loading