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] #41

Merged
merged 32 commits into from
Aug 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 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
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
6 changes: 6 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@ dependencies {
// 태그 제거
implementation("org.jsoup:jsoup:1.15.4")

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

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

}
noArg {
annotation("jakarta.persistence.Entity")
Expand Down
1 change: 0 additions & 1 deletion docker-compose-local.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ services:
- '3306:3306'
volumes:
- db:/var/lib/mysql
- $PWD/db/init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
db:
driver: local
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ services:
args:
PROFILE: ${PROFILE}
ports:
- 80:8080
- 8080:8080
environment:
SPRING_DATASOURCE_URL: "jdbc:mysql://csereal_db_container:3306/${MYSQL_DATABASE}?serverTimezone=Asia/Seoul&useSSL=false&allowPublicKeyRetrieval=true"
SPRING_DATASOURCE_USERNAME: ${MYSQL_USER}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ open class CserealException(msg: String, val status: HttpStatus) : RuntimeExcept
class Csereal400(msg: String) : CserealException(msg, HttpStatus.BAD_REQUEST)
class Csereal404(msg: String) : CserealException(msg, HttpStatus.NOT_FOUND)
class Csereal401(msg: String) : CserealException(msg, HttpStatus.UNAUTHORIZED)
class Csereal409(msg: String) : CserealException(msg, HttpStatus.CONFLICT)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.wafflestudio.csereal.common.aop

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class AuthenticatedStaff

@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.RUNTIME)
annotation class AuthenticatedForReservation
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.wafflestudio.csereal.common.aop

import com.wafflestudio.csereal.common.CserealException
import com.wafflestudio.csereal.core.user.database.Role
import com.wafflestudio.csereal.core.user.database.UserEntity
import com.wafflestudio.csereal.core.user.database.UserRepository
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Before
import org.springframework.security.core.context.SecurityContextHolder
import org.springframework.security.oauth2.core.oidc.user.OidcUser
import org.springframework.stereotype.Component
import org.springframework.web.context.request.RequestAttributes
import org.springframework.web.context.request.RequestContextHolder

@Aspect
@Component
class SecurityAspect(private val userRepository: UserRepository) {

@Before("@annotation(AuthenticatedStaff)")
fun checkStaffAuthentication() {
val user = getLoginUser()

if (user.role != Role.ROLE_STAFF) {
throw CserealException.Csereal401("권한이 없습니다.")
}
}

@Before("@annotation(AuthenticatedForReservation)")
fun checkReservationAuthentication() {
val user = getLoginUser()

if (user.role == null) {
throw CserealException.Csereal401("권한이 없습니다.")
}
}

private fun getLoginUser(): UserEntity {
val authentication = SecurityContextHolder.getContext().authentication
val principal = authentication.principal

if (principal !is OidcUser) {
throw CserealException.Csereal401("로그인이 필요합니다.")
}

val username = principal.idToken.getClaim<String>("username")
val user = userRepository.findByUsername(username) ?: throw CserealException.Csereal404("재로그인이 필요합니다.")

RequestContextHolder.getRequestAttributes()?.setAttribute("loggedInUser", user, RequestAttributes.SCOPE_REQUEST)

return user
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.wafflestudio.csereal.common.controller

import com.wafflestudio.csereal.core.resource.mainImage.database.MainImageEntity

interface ContentEntityType {
fun bringMainImage(): MainImageEntity?
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,8 @@ import com.wafflestudio.csereal.core.about.dto.AboutDto
import com.wafflestudio.csereal.core.about.service.AboutService
import jakarta.validation.Valid
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile

@RequestMapping("/about")
@RestController
Expand All @@ -25,9 +20,10 @@ class AboutController(
@PostMapping("/{postType}")
fun createAbout(
@PathVariable postType: String,
@Valid @RequestBody request: AboutDto
@Valid @RequestPart("request") request: AboutDto,
@RequestPart("image") image: MultipartFile?,
) : ResponseEntity<AboutDto> {
return ResponseEntity.ok(aboutService.createAbout(postType, request))
return ResponseEntity.ok(aboutService.createAbout(postType, request, image))
}

// read 목록이 하나
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.wafflestudio.csereal.core.about.database

import com.wafflestudio.csereal.common.config.BaseTimeEntity
import com.wafflestudio.csereal.common.controller.ContentEntityType
import com.wafflestudio.csereal.core.about.dto.AboutDto
import com.wafflestudio.csereal.core.resource.mainImage.database.MainImageEntity
import jakarta.persistence.*

@Entity(name = "about")
Expand All @@ -14,8 +16,14 @@ class AboutEntity(
var year: Int?,

@OneToMany(mappedBy = "about", cascade = [CascadeType.ALL], orphanRemoval = true)
val locations: MutableList<LocationEntity> = mutableListOf()
) : BaseTimeEntity() {
val locations: MutableList<LocationEntity> = mutableListOf(),

@OneToOne
var mainImage: MainImageEntity? = null,

) : BaseTimeEntity(), ContentEntityType {
override fun bringMainImage(): MainImageEntity? = mainImage

companion object {
fun of(postType: AboutPostType, aboutDto: AboutDto): AboutEntity {
return AboutEntity(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ data class AboutDto(
val year: Int?,
val createdAt: LocalDateTime?,
val modifiedAt: LocalDateTime?,
val locations: List<String>?
val locations: List<String>?,
val imageURL: String?
) {
companion object {
fun of(entity: AboutEntity) : AboutDto = entity.run {
fun of(entity: AboutEntity, imageURL: String?) : AboutDto = entity.run {
AboutDto(
id = this.id,
name = this.name,
Expand All @@ -24,7 +25,8 @@ data class AboutDto(
year = this.year,
createdAt = this.createdAt,
modifiedAt = this.modifiedAt,
locations = this.locations.map { it.name }
locations = this.locations.map { it.name },
imageURL = imageURL
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ import com.wafflestudio.csereal.core.about.database.AboutPostType
import com.wafflestudio.csereal.core.about.database.AboutRepository
import com.wafflestudio.csereal.core.about.database.LocationEntity
import com.wafflestudio.csereal.core.about.dto.AboutDto
import com.wafflestudio.csereal.core.resource.mainImage.service.ImageService
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import org.springframework.web.multipart.MultipartFile

interface AboutService {
fun createAbout(postType: String, request: AboutDto): AboutDto
fun createAbout(postType: String, request: AboutDto, image: MultipartFile?): AboutDto
fun readAbout(postType: String): AboutDto
fun readAllClubs() : List<AboutDto>
fun readAllFacilities() : List<AboutDto>
Expand All @@ -19,10 +21,11 @@ interface AboutService {

@Service
class AboutServiceImpl(
private val aboutRepository: AboutRepository
private val aboutRepository: AboutRepository,
private val imageService: ImageService,
) : AboutService {
@Transactional
override fun createAbout(postType: String, request: AboutDto): AboutDto {
override fun createAbout(postType: String, request: AboutDto, image: MultipartFile?): AboutDto {
val enumPostType = makeStringToEnum(postType)
val newAbout = AboutEntity.of(enumPostType, request)

Expand All @@ -32,23 +35,30 @@ class AboutServiceImpl(
}
}

if(image != null) {
imageService.uploadImage(newAbout, image)
}
aboutRepository.save(newAbout)

return AboutDto.of(newAbout)
val imageURL = imageService.createImageURL(newAbout.mainImage)

return AboutDto.of(newAbout, imageURL)
}

@Transactional(readOnly = true)
override fun readAbout(postType: String): AboutDto {
val enumPostType = makeStringToEnum(postType)
val about = aboutRepository.findByPostType(enumPostType)
val imageURL = imageService.createImageURL(about.mainImage)

return AboutDto.of(about)
return AboutDto.of(about, imageURL)
}

@Transactional(readOnly = true)
override fun readAllClubs(): List<AboutDto> {
val clubs = aboutRepository.findAllByPostTypeOrderByName(AboutPostType.STUDENT_CLUBS).map {
AboutDto.of(it)
val imageURL = imageService.createImageURL(it.mainImage)
AboutDto.of(it, imageURL)
}

return clubs
Expand All @@ -57,7 +67,8 @@ class AboutServiceImpl(
@Transactional(readOnly = true)
override fun readAllFacilities(): List<AboutDto> {
val facilities = aboutRepository.findAllByPostTypeOrderByName(AboutPostType.FACILITIES).map {
AboutDto.of(it)
val imageURL = imageService.createImageURL(it.mainImage)
AboutDto.of(it, imageURL)
}

return facilities
Expand All @@ -66,7 +77,8 @@ class AboutServiceImpl(
@Transactional(readOnly = true)
override fun readAllDirections(): List<AboutDto> {
val directions = aboutRepository.findAllByPostTypeOrderByName(AboutPostType.DIRECTIONS).map {
AboutDto.of(it)
val imageURL = imageService.createImageURL(it.mainImage)
AboutDto.of(it, imageURL)
}

return directions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import com.wafflestudio.csereal.core.member.dto.SimpleProfessorDto
import com.wafflestudio.csereal.core.member.service.ProfessorService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile

@RequestMapping("/professor")
@RestController
Expand All @@ -14,8 +15,11 @@ class ProfessorController(
) {

@PostMapping
fun createProfessor(@RequestBody createProfessorRequest: ProfessorDto): ResponseEntity<ProfessorDto> {
return ResponseEntity.ok(professorService.createProfessor(createProfessorRequest))
fun createProfessor(
@RequestPart("request") createProfessorRequest: ProfessorDto,
@RequestPart("image") image: MultipartFile?,
): ResponseEntity<ProfessorDto> {
return ResponseEntity.ok(professorService.createProfessor(createProfessorRequest, image))
}

@GetMapping("/{professorId}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,8 @@ import com.wafflestudio.csereal.core.member.dto.SimpleStaffDto
import com.wafflestudio.csereal.core.member.dto.StaffDto
import com.wafflestudio.csereal.core.member.service.StaffService
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.*
import org.springframework.web.multipart.MultipartFile

@RequestMapping("/staff")
@RestController
Expand All @@ -20,8 +14,11 @@ class StaffController(
) {

@PostMapping
fun createStaff(@RequestBody createStaffRequest: StaffDto): ResponseEntity<StaffDto> {
return ResponseEntity.ok(staffService.createStaff(createStaffRequest))
fun createStaff(
@RequestPart("request") createStaffRequest: StaffDto,
@RequestPart("image") image: MultipartFile?,
): ResponseEntity<StaffDto> {
return ResponseEntity.ok(staffService.createStaff(createStaffRequest,image))
}

@GetMapping("/{staffId}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package com.wafflestudio.csereal.core.member.database

import com.wafflestudio.csereal.common.config.BaseTimeEntity
import com.wafflestudio.csereal.common.controller.ContentEntityType
import com.wafflestudio.csereal.core.member.dto.ProfessorDto
import com.wafflestudio.csereal.core.research.database.LabEntity
import com.wafflestudio.csereal.core.resource.mainImage.database.MainImageEntity
import jakarta.persistence.*
import java.time.LocalDate

Expand Down Expand Up @@ -39,8 +41,11 @@ class ProfessorEntity(
@OneToMany(mappedBy = "professor", cascade = [CascadeType.ALL], orphanRemoval = true)
val careers: MutableList<CareerEntity> = mutableListOf(),

var imageUri: String? = null
) : BaseTimeEntity() {
@OneToOne
var mainImage: MainImageEntity? = null,

) : BaseTimeEntity(), ContentEntityType {
override fun bringMainImage(): MainImageEntity? = mainImage

companion object {
fun of(professorDto: ProfessorDto): ProfessorEntity {
Expand All @@ -54,7 +59,7 @@ class ProfessorEntity(
phone = professorDto.phone,
fax = professorDto.fax,
email = professorDto.email,
website = professorDto.website
website = professorDto.website,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.wafflestudio.csereal.core.member.database

import com.wafflestudio.csereal.common.config.BaseTimeEntity
import com.wafflestudio.csereal.common.controller.ContentEntityType
import com.wafflestudio.csereal.core.member.dto.StaffDto
import com.wafflestudio.csereal.core.resource.mainImage.database.MainImageEntity
import jakarta.persistence.CascadeType
import jakarta.persistence.Entity
import jakarta.persistence.OneToMany
import jakarta.persistence.OneToOne

@Entity(name = "staff")
class StaffEntity(
Expand All @@ -18,9 +21,11 @@ class StaffEntity(
@OneToMany(mappedBy = "staff", cascade = [CascadeType.ALL], orphanRemoval = true)
val tasks: MutableList<TaskEntity> = mutableListOf(),

var imageUri: String? = null
@OneToOne
var mainImage: MainImageEntity? = null,

) : BaseTimeEntity() {
) : BaseTimeEntity(), ContentEntityType {
override fun bringMainImage(): MainImageEntity? = mainImage

companion object {
fun of(staffDto: StaffDto): StaffEntity {
Expand All @@ -29,7 +34,7 @@ class StaffEntity(
role = staffDto.role,
office = staffDto.office,
phone = staffDto.phone,
email = staffDto.email
email = staffDto.email,
)
}
}
Expand Down
Loading