Skip to content

Commit 5edf247

Browse files
authored
Merge pull request #318 from GSM-MSG/315-feature/query-student-info
token 링크를 클릭하면 기업이 학생 정보를 볼 수 있도록 API를 작성했습니다.
2 parents 45f2189 + bb78faf commit 5edf247

File tree

13 files changed

+246
-4
lines changed

13 files changed

+246
-4
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package team.msg.sms.domain.student.dto.res
2+
3+
import team.msg.sms.domain.languagecertificate.model.LanguageCertificate
4+
import team.msg.sms.domain.prize.dto.res.PrizeResponseData
5+
import team.msg.sms.domain.project.dto.res.ProjectResponseData
6+
import team.msg.sms.domain.student.model.Department
7+
import team.msg.sms.domain.student.model.FormOfEmployment
8+
import team.msg.sms.domain.student.model.MilitaryService
9+
10+
data class DetailStudentInfoTokenResponseData(
11+
val name: String,
12+
val introduce: String,
13+
val portfolioUrl: String?,
14+
val grade: Int,
15+
val classNum: Int,
16+
val number: Int,
17+
val department: Department,
18+
val major: String,
19+
val profileImg: String,
20+
val contactEmail: String,
21+
val gsmAuthenticationScore: Int,
22+
val formOfEmployment: FormOfEmployment,
23+
val regions: List<String>, // 근무지역
24+
val militaryService: MilitaryService,
25+
val salary: Int,
26+
val languageCertificates: List<LanguageCertificate.LanguageCertificateScore>,
27+
val certificates: List<String>,
28+
val techStacks: List<String>,
29+
val projects: List<ProjectResponseData>,
30+
val prizes: List<PrizeResponseData>
31+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package team.msg.sms.domain.student.exception
2+
3+
import team.msg.sms.common.error.SmsException
4+
import team.msg.sms.domain.student.exception.error.StudentErrorCode
5+
6+
object StudentLinkNotFoundException : SmsException(
7+
StudentErrorCode.STUDENT_LINK_NOT_FOUND
8+
)

sms-core/src/main/kotlin/team/msg/sms/domain/student/exception/error/StudentErrorCode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ enum class StudentErrorCode(
1111
STUDENT_NOT_FOUND(ErrorStatus.NOT_FOUND, "학생이 존재하지 않습니다."),
1212
STU_NUM_NOR_RIGHT(ErrorStatus.BAD_REQUEST, "올바르지 않은 학번입니다."),
1313
STUDENT_ALREADY(ErrorStatus.CONFLICT, "학생 정보가 존재하는 유저입니다"),
14+
STUDENT_LINK_NOT_FOUND(ErrorStatus.CONFLICT, "학생 열람 정보가 존재하지 않습니다."),
1415
;
1516

1617
override fun status(): Int = status
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package team.msg.sms.domain.student.service
2+
3+
import team.msg.sms.domain.student.model.Student.StudentWithUserInfo
4+
5+
interface GetStudentLinkService {
6+
fun getStudentUserInfoByToken(token: String) : StudentWithUserInfo
7+
}

sms-core/src/main/kotlin/team/msg/sms/domain/student/service/StudentLinkService.kt

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import team.msg.sms.common.annotation.Service
55
@Service
66
class StudentLinkService (
77
commandStudentLinkService: CommandStudentLinkService,
8-
checkStudentLinkService: CheckStudentLinkService
9-
): CommandStudentLinkService by commandStudentLinkService,
10-
CheckStudentLinkService by checkStudentLinkService
8+
checkStudentLinkService: CheckStudentLinkService,
9+
getStudentLinkService: GetStudentLinkService
10+
) : CommandStudentLinkService by commandStudentLinkService,
11+
CheckStudentLinkService by checkStudentLinkService,
12+
GetStudentLinkService by getStudentLinkService
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package team.msg.sms.domain.student.service.impl
2+
3+
import team.msg.sms.common.annotation.Service
4+
import team.msg.sms.domain.student.exception.StudentLinkNotFoundException
5+
import team.msg.sms.domain.student.exception.StudentNotFoundException
6+
import team.msg.sms.domain.student.model.Student.StudentWithUserInfo
7+
import team.msg.sms.domain.student.service.GetStudentLinkService
8+
import team.msg.sms.domain.student.spi.StudentLinkPort
9+
import team.msg.sms.domain.student.spi.StudentPort
10+
11+
@Service
12+
class GetStudentLinkServiceImpl(
13+
private val studentLinkPort: StudentLinkPort,
14+
private val studentPort: StudentPort
15+
) : GetStudentLinkService {
16+
override fun getStudentUserInfoByToken(token: String): StudentWithUserInfo {
17+
val studentId = studentLinkPort.findStudentIdByToken(token) ?: throw StudentNotFoundException
18+
val studentWithUserInfo = studentPort.queryStudentWithUserInfoById(studentId) ?: throw StudentLinkNotFoundException
19+
20+
return studentWithUserInfo
21+
}
22+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package team.msg.sms.domain.student.spi
22

3+
import java.util.UUID
4+
35
interface QueryStudentLinkPort {
46
fun existsByToken(token: String): Boolean
7+
fun findStudentIdByToken(token: String): UUID?
58
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package team.msg.sms.domain.student.usecase
2+
3+
import team.msg.sms.common.annotation.UseCase
4+
import team.msg.sms.common.util.PrizeUtil.generatePrizeResponseData
5+
import team.msg.sms.common.util.ProjectUtil.generateProjectResponseData
6+
import team.msg.sms.domain.certificate.service.CertificateService
7+
import team.msg.sms.domain.file.service.ImageService
8+
import team.msg.sms.domain.languagecertificate.model.LanguageCertificate
9+
import team.msg.sms.domain.languagecertificate.service.LanguageCertificateService
10+
import team.msg.sms.domain.prize.service.PrizeService
11+
import team.msg.sms.domain.project.service.ProjectLinkService
12+
import team.msg.sms.domain.project.service.ProjectService
13+
import team.msg.sms.domain.project.service.ProjectTechStackService
14+
import team.msg.sms.domain.region.service.RegionService
15+
import team.msg.sms.domain.student.dto.res.DetailStudentInfoTokenResponseData
16+
import team.msg.sms.domain.student.model.Student
17+
import team.msg.sms.domain.student.model.StudentTechStack
18+
import team.msg.sms.domain.student.service.StudentLinkService
19+
import team.msg.sms.domain.student.service.StudentService
20+
import team.msg.sms.domain.student.service.StudentTechStackService
21+
import team.msg.sms.domain.techstack.model.TechStack
22+
import team.msg.sms.domain.techstack.service.TechStackService
23+
24+
@UseCase
25+
class StudentInfoTokenUseCase(
26+
private val studentService: StudentService,
27+
private val certificateService: CertificateService,
28+
private val studentTechStackService: StudentTechStackService,
29+
private val projectService: ProjectService,
30+
private val techStackService: TechStackService,
31+
private val projectTechStackService: ProjectTechStackService,
32+
private val projectLinkService: ProjectLinkService,
33+
private val imageService: ImageService,
34+
private val languageCertificateService: LanguageCertificateService,
35+
private val regionService: RegionService,
36+
private val prizeService: PrizeService,
37+
private val studentLinkService: StudentLinkService
38+
) {
39+
fun execute(token: String): DetailStudentInfoTokenResponseData {
40+
val student: Student.StudentWithUserInfo = studentLinkService.getStudentUserInfoByToken(token)
41+
42+
val projects = projectService.getAllProjectByStudentId(studentId = student.id)
43+
val certificates = certificateService.getCertificateByUuid(student.id).map { it.certificateName }
44+
val languageCertificates =
45+
languageCertificateService.getLanguageCertificateByStudentUuid(student.id)
46+
.map { toLanguageCertificateScore(languageCertificate = it) }
47+
val regions = regionService.getRegionByStudentUuid(student.id).map { it.region }
48+
val techStacks = techStackService.getAllTechStack()
49+
val prizes = prizeService.getAllPrizeByStudentId(studentId = student.id)
50+
val studentTechStacks = studentTechStackService.getStudentTechStackByStudentId(student.id)
51+
52+
return DetailStudentInfoTokenResponseData(
53+
name = student.name,
54+
introduce = student.introduce,
55+
portfolioUrl = student.portfolioUrl,
56+
grade = student.stuNum.substring(0, 1).toInt(),
57+
classNum = student.stuNum.substring(1, 2).toInt(),
58+
number = student.stuNum.substring(2, 4).toInt(),
59+
department = student.department,
60+
major = student.major,
61+
profileImg = student.profileImgUrl,
62+
contactEmail = student.contactEmail,
63+
gsmAuthenticationScore = student.gsmAuthenticationScore,
64+
formOfEmployment = student.formOfEmployment,
65+
regions = regions,
66+
militaryService = student.militaryService,
67+
salary = student.salary,
68+
languageCertificates = languageCertificates,
69+
certificates = certificates,
70+
techStacks = studentTechStacks.map {
71+
toStudentTechStacks(techStacks, it)?.stack ?: ""
72+
},
73+
projects = generateProjectResponseData(
74+
projects = projects,
75+
projectLinkService = projectLinkService,
76+
projectTechStackService = projectTechStackService,
77+
imageService = imageService,
78+
techStacks = techStacks
79+
),
80+
prizes = generatePrizeResponseData(
81+
prizes = prizes
82+
)
83+
)
84+
}
85+
86+
private fun toLanguageCertificateScore(
87+
languageCertificate: LanguageCertificate,
88+
): LanguageCertificate.LanguageCertificateScore =
89+
LanguageCertificate.LanguageCertificateScore(
90+
languageCertificateName = languageCertificate.languageCertificateName,
91+
score = languageCertificate.score,
92+
)
93+
94+
private fun toStudentTechStacks(techStacks: List<TechStack>, studentTechStack: StudentTechStack): TechStack? =
95+
techStacks.find { it.id == studentTechStack.techStackId }
96+
}
97+

sms-infrastructure/src/main/kotlin/team/msg/sms/global/security/SecurityConfig.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ class SecurityConfig(
6060

6161
.antMatchers(HttpMethod.POST, "/student").hasAuthority(STUDENT)
6262
.antMatchers(HttpMethod.GET, "/student").permitAll()
63-
.antMatchers(HttpMethod.GET, "/student/{uuid}").hasAnyAuthority(STUDENT, TEACHER)
6463
.antMatchers(HttpMethod.POST, "/student/link").hasAuthority(TEACHER)
64+
.antMatchers(HttpMethod.GET, "/student/link").permitAll()
65+
.antMatchers(HttpMethod.GET, "/student/{uuid}").hasAnyAuthority(STUDENT, TEACHER)
6566
.antMatchers(HttpMethod.GET, "/student/anonymous/{uuid}").permitAll()
6667

6768
.antMatchers(HttpMethod.POST, "/teacher/common").hasAuthority(TEACHER)

sms-persistence/src/main/kotlin/team/msg/sms/persistence/student/StudentLinkPersistenceAdapter.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import team.msg.sms.persistence.student.mapper.toDomain
77
import team.msg.sms.persistence.student.mapper.toEntity
88
import team.msg.sms.persistence.student.repository.StudentJpaRepository
99
import team.msg.sms.persistence.student.repository.StudentLinkRepository
10+
import java.util.*
1011

1112
@Component
1213
class StudentLinkPersistenceAdapter(
@@ -21,4 +22,8 @@ class StudentLinkPersistenceAdapter(
2122
override fun existsByToken(token: String): Boolean {
2223
return studentLinkRepository.existsByToken(token)
2324
}
25+
26+
override fun findStudentIdByToken(token: String): UUID? {
27+
return studentLinkRepository.findByToken(token)?.studentId
28+
}
2429
}

sms-persistence/src/main/kotlin/team/msg/sms/persistence/student/repository/StudentLinkRepository.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ import team.msg.sms.persistence.student.entity.StudentLinkEntity
77
@Repository
88
interface StudentLinkRepository : CrudRepository<StudentLinkEntity, Long> {
99
fun existsByToken(token: String): Boolean
10+
fun findByToken(token: String): StudentLinkEntity?
1011
}

sms-presentation/src/main/kotlin/team/msg/sms/domain/student/StudentWebAdapter.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class StudentWebAdapter(
2020
private val studentInfoAnonymousUseCase: StudentInfoAnonymousUseCase,
2121
private val studentInfoDetailUseCase: StudentInfoDetailUseCase,
2222
private val studentInfoTeacherUseCase: StudentInfoTeacherUseCase,
23+
private val studentInfoTokenUseCase: StudentInfoTokenUseCase,
2324
private val modifyStudentInfoUseCase: ModifyStudentInfoUseCase,
2425
private val createStudentLinkUseCase: CreateStudentLinkUseCase
2526
) {
@@ -43,6 +44,11 @@ class StudentWebAdapter(
4344
.let { ResponseEntity.ok(it.toResponse()) }
4445
}
4546

47+
@GetMapping("/link")
48+
fun findByToken(@RequestParam token: String): ResponseEntity<DetailStudentInfoTokenWebResponse> =
49+
studentInfoTokenUseCase.execute(token)
50+
.let { ResponseEntity.ok(it.toResponse()) }
51+
4652
@PutMapping
4753
fun modifyStudentInfo(@RequestBody modifyStudentInfoWebRequest: ModifyStudentInfoWebRequest) {
4854
modifyStudentInfoUseCase.execute(modifyStudentInfoWebRequest.toData())
@@ -138,6 +144,31 @@ class StudentWebAdapter(
138144
token = this.token
139145
)
140146

147+
fun DetailStudentInfoTokenResponseData.toResponse(): DetailStudentInfoTokenWebResponse =
148+
DetailStudentInfoTokenWebResponse(
149+
name = this.name,
150+
introduce = this.introduce,
151+
portfolioUrl = this.portfolioUrl,
152+
grade = this.grade,
153+
classNum = this.classNum,
154+
number = this.number,
155+
department = this.department,
156+
major = this.major,
157+
profileImgUrl = this.profileImg,
158+
profileImg = this.profileImg,
159+
contactEmail = this.contactEmail,
160+
gsmAuthenticationScore = this.gsmAuthenticationScore,
161+
formOfEmployment = this.formOfEmployment,
162+
regions = this.regions,
163+
militaryService = this.militaryService,
164+
salary = this.salary,
165+
languageCertificates = this.languageCertificates,
166+
certificates = this.certificates,
167+
techStacks = this.techStacks,
168+
projects = this.projects,
169+
prizes = this.prizes
170+
)
171+
141172
private fun isValidUUID(uuid: String): Boolean {
142173
return try {
143174
UUID.fromString(uuid)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package team.msg.sms.domain.student.dto.res
2+
3+
import team.msg.sms.domain.languagecertificate.model.LanguageCertificate
4+
import team.msg.sms.domain.prize.dto.res.PrizeResponseData
5+
import team.msg.sms.domain.project.dto.res.ProjectResponseData
6+
import team.msg.sms.domain.student.model.Department
7+
import team.msg.sms.domain.student.model.FormOfEmployment
8+
import team.msg.sms.domain.student.model.MilitaryService
9+
10+
data class DetailStudentInfoTokenWebResponse(
11+
val name: String,
12+
val introduce: String,
13+
val portfolioUrl: String?,
14+
val grade: Int,
15+
val classNum: Int,
16+
val number: Int,
17+
val department: Department,
18+
val major: String,
19+
val profileImgUrl: String,
20+
@Deprecated(message = "전 Beta Version 사용자들의 접근성을 개방하기 위하여 현재버전에서는 사용하지 않습니다.")
21+
val profileImg: String,
22+
val contactEmail: String,
23+
val gsmAuthenticationScore: Int,
24+
val formOfEmployment: FormOfEmployment,
25+
val regions: List<String>, // 근무지역
26+
val militaryService: MilitaryService,
27+
val salary: Int,
28+
val languageCertificates: List<LanguageCertificate.LanguageCertificateScore>,
29+
val certificates: List<String>,
30+
val techStacks: List<String>,
31+
val projects: List<ProjectResponseData>,
32+
val prizes: List<PrizeResponseData>
33+
)

0 commit comments

Comments
 (0)