Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
e13a605
chore(#42): gitkeep 제거
ByunDohwi Apr 1, 2026
621873f
chore(#42): gitkeep 제거
ByunDohwi Apr 1, 2026
06540c3
Merge remote-tracking branch 'origin/feat/(#41)-봉사-조회-및-역할-지정-api-추가'…
ByunDohwi Apr 1, 2026
2eb3690
feat(#41): userActivity
ByunDohwi Apr 1, 2026
1672417
chore(#41): gitkeep 삭제
ByunDohwi Apr 1, 2026
c0bf658
feat(#41): 컬럼 추가 및 역할 추가 api
ByunDohwi Apr 1, 2026
387a04f
chore(#42): gitkeep 삭제
ByunDohwi Apr 1, 2026
a0fb13a
feat(#41): 봉사 상세 조회 api 추가
ByunDohwi Apr 1, 2026
1695d52
feat(#42): schema 수정
ByunDohwi Apr 2, 2026
21e486a
feat(#41): 봉사활동 리스트 조회 api 추가
ByunDohwi Apr 2, 2026
1dfccad
fix(#41): 유저 활동 생성 요청 바디 매핑 추가
ByunDohwi Apr 2, 2026
e74e054
sytle(#42): lint format
ByunDohwi Apr 2, 2026
28b7a7c
feat(#41): status 추가
ByunDohwi Apr 2, 2026
09e7c29
feat(#41): schema 수정
ByunDohwi Apr 2, 2026
c9d134c
feat(#41): 봉사활동 리스트 조회 api 추가
ByunDohwi Apr 2, 2026
9cfd30e
fix(#41): 유저 활동 생성 요청 바디 매핑 추가
ByunDohwi Apr 2, 2026
272e3b4
sytle(#41): lint format
ByunDohwi Apr 2, 2026
21119a1
feat(#41): status 추가
ByunDohwi Apr 2, 2026
91b7e94
Merge remote-tracking branch 'origin/feat/(#41)-봉사-조회-및-역할-지정-api-추가'…
ByunDohwi Apr 2, 2026
0306eac
chore(#41): valid 추가
ByunDohwi Apr 3, 2026
631094e
fix(#41): delete response status 변경
ByunDohwi Apr 3, 2026
e2d4cd7
chore(#41): userId 삭제
ByunDohwi Apr 3, 2026
c276ebf
chore(#41): 내부/외부 DTO 분리
ByunDohwi Apr 4, 2026
f9a1e0a
chore(#41): 환경변수 삭제
ByunDohwi Apr 5, 2026
66b5ea3
chore(#41): 환경변수 파일 ignore 추가
ByunDohwi Apr 5, 2026
0558d05
chore(#41): 환경변수 파일 ignore 추가
ByunDohwi Apr 5, 2026
40aedab
chore(#41): exception 하위 도메인 패키지 생성
ByunDohwi Apr 6, 2026
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
11 changes: 10 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,13 @@ logs/
**/application-secret.properties

### Firebase ###
**/firebase-service-account.json
**/firebase-service-account.json

### Environment Variables ###
.env
.env.local
.env.dev
.env.prod
.env.example
CLAUDE.md
run.sh
25 changes: 25 additions & 0 deletions finda-volunteer/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
kotlin("plugin.spring")
id("org.springframework.boot")
id("io.spring.dependency-management")
id("com.google.protobuf") version "0.9.4"
}

group = "finda"
Expand All @@ -27,7 +28,13 @@ dependencies {
implementation(Dependencies.SPRING_SECURITY)
implementation(Dependencies.JACKSON)
implementation(Dependencies.JACKSON_TYPE)
implementation(Dependencies.SPRING_VALIDITY)
implementation(Dependencies.LIQUIBASE)
implementation(Dependencies.GRPC_CLIENT)
implementation(Dependencies.GRPC_PROTOBUF)
implementation(Dependencies.GRPC_STUB)
implementation(Dependencies.PROTOBUF_JAVA)
compileOnly(Dependencies.JAVAX_ANNOTATION)

// JWT
implementation(Dependencies.JWT_API)
Expand All @@ -38,6 +45,24 @@ dependencies {
implementation(project(":finda-security-common"))
}

protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.24.0"
}
plugins {
create("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.59.0"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
create("grpc")
}
}
}
}

kotlin {
compilerOptions {
freeCompilerArgs.addAll("-Xjsr305=strict", "-Xannotation-default-target=param-property")
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,8 +1,23 @@
package finda.findavolunteer.adapter.`in`.activity

import finda.findavolunteer.adapter.`in`.activity.dto.request.CreateUserActivityRequest
import finda.findavolunteer.adapter.`in`.activity.mapper.toCommand
import finda.findavolunteer.application.port.`in`.activity.CreateUserActivityUseCase
import jakarta.validation.Valid
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

@RestController
@RequestMapping("/activities")
class ActivityWebAdapter
class ActivityWebAdapter(
private val createUserActivityUseCase: CreateUserActivityUseCase
) {
@PostMapping("/user")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResponseStatus 추가하시면 좋을 것 같아요

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fun createUserActivity(
@Valid @RequestBody
request: CreateUserActivityRequest
) =
createUserActivityUseCase.execute(request.toCommand())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package finda.findavolunteer.adapter.`in`.activity.dto.request

import jakarta.validation.Valid
import jakarta.validation.constraints.NotEmpty
import java.util.*

data class CreateUserActivityRequest(
@field:Valid
@field:NotEmpty
val userActivityList: List<UserActivity>
)

data class UserActivity(
val userId: UUID,
val activityId: UUID
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package finda.findavolunteer.adapter.`in`.activity.mapper

import finda.findavolunteer.adapter.`in`.activity.dto.request.CreateUserActivityRequest
import finda.findavolunteer.application.port.`in`.activity.dto.request.CreateUserActivityCommand
import finda.findavolunteer.application.port.`in`.activity.dto.request.UserActivityCommand

fun CreateUserActivityRequest.toCommand(): CreateUserActivityCommand =
CreateUserActivityCommand(
userActivityCommandList = userActivityList.map {
UserActivityCommand(
userId = it.userId,
activityId = it.activityId
)
}
)
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package finda.findavolunteer.adapter.`in`.volunteer

import finda.findavolunteer.adapter.`in`.volunteer.dto.request.CreateVolunteerRequest
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.VolunteerDetailResponse
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.VolunteerListResponse
import finda.findavolunteer.adapter.`in`.volunteer.mapper.toCommand
import finda.findavolunteer.adapter.`in`.volunteer.mapper.toResponse
import finda.findavolunteer.application.port.`in`.volunteer.CreateVolunteerUseCase
import finda.findavolunteer.application.port.`in`.volunteer.DeleteVolunteerUseCase
import finda.findavolunteer.application.port.`in`.volunteer.VolunteerDetailUseCase
import finda.findavolunteer.application.port.`in`.volunteer.VolunteerListUseCase
import finda.findavolunteer.domain.volunteer.enum.VolunteerStatus
import org.springframework.http.HttpStatus
import org.springframework.web.bind.annotation.DeleteMapping
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
Expand All @@ -17,11 +26,31 @@ import java.util.UUID
@RequestMapping("/volunteers")
class VolunteerWebAdapter(
val createVolunteerUseCase: CreateVolunteerUseCase,
val deleteVolunteerUseCase: DeleteVolunteerUseCase
val deleteVolunteerUseCase: DeleteVolunteerUseCase,
val volunteerDetailUseCase: VolunteerDetailUseCase,
val volunteerListUseCase: VolunteerListUseCase
) {
@PostMapping
@ResponseStatus(value = HttpStatus.CREATED)
fun createVolunteer(@RequestBody request: CreateVolunteerRequest) = createVolunteerUseCase.execute(request)
fun createVolunteer(@RequestBody request: CreateVolunteerRequest) =
createVolunteerUseCase.execute(request.toCommand())

@GetMapping("/{volunteerId}")
@ResponseStatus(value = HttpStatus.OK)
fun getVolunteer(@PathVariable volunteerId: UUID): VolunteerDetailResponse =
volunteerDetailUseCase.execute(volunteerId).toResponse()

@GetMapping
@ResponseStatus(value = HttpStatus.OK)
fun getVolunteers(
@RequestParam(required = false) status: VolunteerStatus?,
@RequestParam(required = false) year: Int?,
@RequestParam(required = false) sortBy: String?
): List<VolunteerListResponse> = volunteerListUseCase.execute(
status = status,
year = year,
sortBy = sortBy
).map { it.toResponse() }

@DeleteMapping
@ResponseStatus(value = HttpStatus.NO_CONTENT)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package finda.findavolunteer.adapter.`in`.volunteer.dto.response

import finda.findavolunteer.domain.participation.enum.ParticipationStatus
import finda.findavolunteer.domain.volunteer.enum.CycleType
import finda.findavolunteer.domain.volunteer.enum.GroupVolunteerType
import finda.findavolunteer.domain.volunteer.enum.VolunteerStatus
import finda.findavolunteer.domain.volunteer.enum.VolunteerType
import finda.findavolunteer.domain.volunteer.enum.Weekday
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.util.UUID

data class VolunteerDetailResponse(
val volunteerId: UUID,
val title: String,
val description: String,
val status: VolunteerStatus,
val personnel: Int,
val unitVolunteerHours: Float,
val applicationStartDate: LocalDate,
val applicationEndDate: LocalDate,
val workStartDate: LocalDate,
val workEndDate: LocalDate,
val cycleType: CycleType,
val weekdays: List<Weekday>,
val monthDate: Int?,
val remindTime: LocalTime,
val groupVolunteerType: GroupVolunteerType,
val volunteerType: VolunteerType,
val writerUserId: UUID,
val schedules: List<VolunteerScheduleResponse>,
val studentParticipations: List<StudentParticipationResponse>,
val activities: List<ActivityResponse>,
val userActivities: List<UserActivityResponse>
)

data class VolunteerScheduleResponse(
val scheduleId: UUID,
val scheduleDate: LocalDate
)

data class StudentParticipationResponse(
val userId: UUID,
val name: String,
val status: ParticipationStatus,
val participatedAt: LocalDateTime
)

data class ActivityResponse(
val activityId: UUID,
val activityName: String
)

data class UserActivityResponse(
val userId: UUID,
val userName: String,
val activityId: UUID
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package finda.findavolunteer.adapter.`in`.volunteer.dto.response

import java.time.LocalDate
import java.util.UUID

data class VolunteerListResponse(
val volunteerId: UUID,
val title: String,
val workStartDate: LocalDate,
val workEndDate: LocalDate,
val unitVolunteerHours: Float
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package finda.findavolunteer.adapter.`in`.volunteer.mapper

import finda.findavolunteer.adapter.`in`.volunteer.dto.request.CreateVolunteerRequest
import finda.findavolunteer.application.port.`in`.volunteer.dto.request.CreateVolunteerCommand
import finda.findavolunteer.application.port.`in`.volunteer.dto.request.VolunteerDateCommand

fun CreateVolunteerRequest.toCommand(): CreateVolunteerCommand =
CreateVolunteerCommand(
personnel = personnal,
title = title,
description = description,
unitVolunteerTime = unitVolunteerTime,
applicationDateCommand = VolunteerDateCommand(
startDate = applicationDate.startDate,
endDate = applicationDate.endDate
),
workDateCommand = VolunteerDateCommand(
startDate = workDate.startDate,
endDate = workDate.endDate
),
cycle = cycle,
volunteerDateList = volunteerDate,
teacherIdList = teachers,
studentIdList = students,
remindTime = remindTime,
volunteerType = volunteerType,
groupVolunteerType = groupVolunteerType,
activityNameList = activity,
weekdayList = weekdays,
monthDate = monthDate
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package finda.findavolunteer.adapter.`in`.volunteer.mapper

import finda.findavolunteer.adapter.`in`.volunteer.dto.response.ActivityResponse
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.StudentParticipationResponse
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.UserActivityResponse
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.VolunteerDetailResponse
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.VolunteerListResponse
import finda.findavolunteer.adapter.`in`.volunteer.dto.response.VolunteerScheduleResponse
import finda.findavolunteer.application.port.`in`.volunteer.dto.response.VolunteerDetailResult
import finda.findavolunteer.application.port.`in`.volunteer.dto.response.VolunteerListResult

fun VolunteerDetailResult.toResponse(): VolunteerDetailResponse =
VolunteerDetailResponse(
volunteerId = volunteerId,
title = title,
description = description,
status = status,
personnel = personnel,
unitVolunteerHours = unitVolunteerHours,
applicationStartDate = applicationStartDate,
applicationEndDate = applicationEndDate,
workStartDate = workStartDate,
workEndDate = workEndDate,
cycleType = cycleType,
weekdays = weekdayList,
monthDate = monthDate,
remindTime = remindTime,
groupVolunteerType = groupVolunteerType,
volunteerType = volunteerType,
writerUserId = writerUserId,
schedules = scheduleResultList.map {
VolunteerScheduleResponse(
scheduleId = it.scheduleId,
scheduleDate = it.scheduleDate
)
},
studentParticipations = studentParticipationResultList.map {
StudentParticipationResponse(
userId = it.userId,
name = it.name,
status = it.status,
participatedAt = it.participatedAt
)
},
activities = activityResultList.map {
ActivityResponse(
activityId = it.activityId,
activityName = it.activityName
)
},
userActivities = userActivityResultList.map {
UserActivityResponse(
userId = it.userId,
userName = it.userName,
activityId = it.activityId
)
}
)

fun VolunteerListResult.toResponse(): VolunteerListResponse =
VolunteerListResponse(
volunteerId = volunteerId,
title = title,
workStartDate = workStartDate,
workEndDate = workEndDate,
unitVolunteerHours = unitVolunteerHours
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package finda.findavolunteer.adapter.out.grpc

import finda.findaauth.adapter.`in`.grpc.AuthServiceGrpc
import finda.findaauth.adapter.`in`.grpc.UserRequest
import finda.findavolunteer.application.port.out.user.UserQueryPort
import io.grpc.Status
import io.grpc.StatusRuntimeException
import net.devh.boot.grpc.client.inject.GrpcClient
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Component
import java.util.UUID
import java.util.concurrent.TimeUnit

@Component
class UserGrpcAdapter : UserQueryPort {
private val log = LoggerFactory.getLogger(javaClass)

@GrpcClient("auth-service")
private lateinit var authServiceStub: AuthServiceGrpc.AuthServiceBlockingStub

override fun getUserName(userId: UUID): String? {
return try {
authServiceStub
.withDeadlineAfter(2L, TimeUnit.SECONDS)
.getUserName(
UserRequest.newBuilder()
.setUserId(userId.toString())
.build()
)
.userName
} catch (e: StatusRuntimeException) {
when (e.status.code) {
Status.Code.NOT_FOUND -> null
else -> {
log.error("gRPC getUserName failed.", e)
throw e
}
}
}
}
}
Loading