Skip to content

Commit e0a5681

Browse files
authored
Merge pull request #430 from projects200/feat/preferred-exercise
[Feat] 선호 운동 개발
2 parents 2d46e99 + e6d316a commit e0a5681

File tree

54 files changed

+1899
-73
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1899
-73
lines changed

app/src/main/java/com/project200/undabang/main/MainActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ class MainActivity : AppCompatActivity(), BottomNavigationController {
138138
com.project200.undabang.feature.profile.R.id.urlFormFragment,
139139
com.project200.undabang.feature.profile.R.id.notificationFragment,
140140
com.project200.undabang.feature.profile.R.id.blockMembersFragment,
141+
com.project200.undabang.feature.profile.R.id.preferredExerciseFragment,
141142
com.project200.undabang.feature.matching.R.id.matchingProfileFragment,
142143
com.project200.undabang.feature.matching.R.id.exercisePlaceFragment,
143144
com.project200.undabang.feature.matching.R.id.exercisePlaceSearchFragment,
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.project200.common.constants
2+
3+
import androidx.annotation.StringRes
4+
import com.project200.undabang.common.R
5+
6+
enum class DayOfWeek(
7+
val index: Int,
8+
@StringRes val resId: Int,
9+
) {
10+
MON(0, R.string.day_mon),
11+
TUE(1, R.string.day_tue),
12+
WED(2, R.string.day_wed),
13+
THU(3, R.string.day_thu),
14+
FRI(4, R.string.day_fri),
15+
SAT(5, R.string.day_sat),
16+
SUN(6, R.string.day_sun),
17+
;
18+
19+
companion object {
20+
fun getByIndex(index: Int): DayOfWeek? = entries.find { it.index == index }
21+
}
22+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package com.project200.common.utils
2+
3+
import android.content.Context
4+
import com.project200.common.constants.DayOfWeek
5+
import com.project200.undabang.common.R
6+
import dagger.hilt.android.qualifiers.ApplicationContext
7+
import javax.inject.Inject
8+
9+
class PreferredExerciseDayFormatter
10+
@Inject
11+
constructor(
12+
@ApplicationContext private val context: Context,
13+
) {
14+
/**
15+
* 월요일부터 일요일까지의 선택 여부를 담은 Boolean 리스트를
16+
* 지정된 규칙에 따라 문자열로 포맷합니다.
17+
* @param days 크기가 7인 Boolean 리스트 [월, 화, 수, 목, 금, 토, 일] 순서
18+
* @return 포맷팅된 요일 문자열
19+
*/
20+
fun formatDaysOfWeek(days: List<Boolean>): String {
21+
if (days.size != 7) return ""
22+
23+
// 모든 요일이 포함될 경우 "매일"로 표기
24+
if (days.all { it }) return context.getString(R.string.day_everyday)
25+
26+
val resultParts = mutableListOf<String>()
27+
val weekdays = days.subList(0, 5) // 월요일부터 금요일까지
28+
val weekend = days.subList(5, 7) // 토요일, 일요일
29+
30+
// 평일이 모두 포함될 경우 "평일"로 표기
31+
if (weekdays.all { it }) {
32+
resultParts.add(context.getString(R.string.day_weekdays))
33+
} else {
34+
// 각각 해당하는 평일 날짜를 추가
35+
weekdays.forEachIndexed { index, isSelected ->
36+
if (isSelected) {
37+
addDayName(index, resultParts)
38+
}
39+
}
40+
}
41+
42+
// 주말이 모두 포함될 경우 "주말"로 표기
43+
if (weekend.all { it }) {
44+
resultParts.add(context.getString(R.string.day_weekend))
45+
} else {
46+
// 각각 해당하는 주말 날짜를 추가
47+
weekend.forEachIndexed { index, isSelected ->
48+
if (isSelected) {
49+
addDayName(index + 5, resultParts)
50+
}
51+
}
52+
}
53+
54+
return resultParts.joinToString(", ")
55+
}
56+
57+
private fun addDayName(
58+
index: Int,
59+
resultList: MutableList<String>,
60+
) {
61+
DayOfWeek.getByIndex(index)?.let { day ->
62+
resultList.add(context.getString(day.resId))
63+
}
64+
}
65+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<!-- 요일 -->
4+
<string name="day_mon">월</string>
5+
<string name="day_tue">화</string>
6+
<string name="day_wed">수</string>
7+
<string name="day_thu">목</string>
8+
<string name="day_fri">금</string>
9+
<string name="day_sat">토</string>
10+
<string name="day_sun">일</string>
11+
12+
<!-- 그룹 표기 -->
13+
<string name="day_everyday">매일</string>
14+
<string name="day_weekdays">평일</string>
15+
<string name="day_weekend">주말</string>
16+
</resources>

data/src/main/java/com/project200/data/api/ApiService.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.project200.data.api
22

33
import com.project200.data.dto.BaseResponse
44
import com.project200.data.dto.CustomTimerIdDTO
5+
import com.project200.data.dto.DeletePreferredExerciseDTO
56
import com.project200.data.dto.EditExercisePlaceDTO
67
import com.project200.data.dto.ExerciseIdDto
78
import com.project200.data.dto.ExpectedScoreInfoDTO
@@ -20,6 +21,8 @@ import com.project200.data.dto.GetMatchingMembersDto
2021
import com.project200.data.dto.GetMatchingProfileDTO
2122
import com.project200.data.dto.GetNewChattingMessagesDTO
2223
import com.project200.data.dto.GetOpenChatUrlDTO
24+
import com.project200.data.dto.GetPreferredExerciseDTO
25+
import com.project200.data.dto.GetPreferredExerciseTypeDTO
2326
import com.project200.data.dto.GetProfileDTO
2427
import com.project200.data.dto.GetProfileImageResponseDto
2528
import com.project200.data.dto.GetScoreDTO
@@ -37,6 +40,7 @@ import com.project200.data.dto.PostExerciseRequestDto
3740
import com.project200.data.dto.PostExerciseResponseDTO
3841
import com.project200.data.dto.PostLoginRequest
3942
import com.project200.data.dto.PostMessageResponse
43+
import com.project200.data.dto.PostPreferredExerciseDTO
4044
import com.project200.data.dto.PostSignUpData
4145
import com.project200.data.dto.PostSignUpRequest
4246
import com.project200.data.dto.PutProfileRequest
@@ -49,6 +53,7 @@ import okhttp3.MultipartBody
4953
import retrofit2.http.Body
5054
import retrofit2.http.DELETE
5155
import retrofit2.http.GET
56+
import retrofit2.http.HTTP
5257
import retrofit2.http.Header
5358
import retrofit2.http.Multipart
5459
import retrofit2.http.PATCH
@@ -170,6 +175,38 @@ interface ApiService {
170175
@AccessTokenApi
171176
suspend fun getBlockedMembers(): BaseResponse<List<GetBlockedMemberDTO>>
172177

178+
/** 선호 운동 */
179+
// 선호 운동 조회
180+
@GET("api/v1/preferred-exercises")
181+
@AccessTokenApi
182+
suspend fun getPreferredExercises(): BaseResponse<List<GetPreferredExerciseDTO>>
183+
184+
// 선호 운동 종류 조회
185+
@GET("api/v1/exercise-types")
186+
@AccessTokenApi
187+
suspend fun getPreferredExerciseTypes(): BaseResponse<List<GetPreferredExerciseTypeDTO>>
188+
189+
// 선호 운동 생성
190+
@POST("api/v1/preferred-exercises")
191+
@AccessTokenApi
192+
suspend fun postPreferredExercises(
193+
@Body preferredExercises: List<PostPreferredExerciseDTO>,
194+
): BaseResponse<Unit?>
195+
196+
// 선호 운동 삭제
197+
@HTTP(method = "DELETE", path = "api/v1/preferred-exercises", hasBody = true)
198+
@AccessTokenApi
199+
suspend fun deletePreferredExercises(
200+
@Body preferredExerciseIds: DeletePreferredExerciseDTO,
201+
): BaseResponse<Unit?>
202+
203+
// 선호 운동 수정
204+
@PATCH("api/v1/preferred-exercises")
205+
@AccessTokenApi
206+
suspend fun patchPreferredExercises(
207+
@Body preferredExercises: List<PostPreferredExerciseDTO>,
208+
): BaseResponse<Unit?>
209+
173210
/** 운동 기록 */
174211
// 구간별 운동 기록 횟수 조회
175212
@GET("api/v1/exercises/count")

data/src/main/java/com/project200/data/dto/MatchingDTO.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ data class GetMatchingProfileDTO(
3131
val yearlyExerciseDays: Int,
3232
val exerciseCountInLast30Days: Int,
3333
val exerciseScore: Int,
34-
val preferredExercises: List<PreferredExerciseDTO>,
34+
val preferredExercises: List<GetProfilePreferredExerciseDTO>,
3535
)
3636

3737
@JsonClass(generateAdapter = true)

data/src/main/java/com/project200/data/dto/MemberDTO.kt

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,45 @@ data class GetProfileDTO(
2121
val yearlyExerciseDays: Int,
2222
val exerciseCountInLast30Days: Int,
2323
val exerciseScore: Int,
24-
val preferredExercises: List<PreferredExerciseDTO>,
24+
val preferredExercises: List<GetProfilePreferredExerciseDTO>,
2525
)
2626

2727
@JsonClass(generateAdapter = true)
28-
data class PreferredExerciseDTO(
29-
val preferredExerciseId: Int,
28+
data class GetProfilePreferredExerciseDTO(
29+
val preferredExerciseId: Long,
3030
val name: String,
3131
val skillLevel: String,
3232
val daysOfWeek: List<Boolean>,
33-
val imageUrl: String,
33+
val imageUrl: String?,
34+
)
35+
36+
@JsonClass(generateAdapter = true)
37+
data class GetPreferredExerciseDTO(
38+
val preferredExerciseId: Long,
39+
val exerciseTypeId: Long,
40+
val exerciseName: String,
41+
val skillLevel: String,
42+
val daysOfWeek: List<Boolean>,
43+
val imageUrl: String?,
44+
)
45+
46+
@JsonClass(generateAdapter = true)
47+
data class GetPreferredExerciseTypeDTO(
48+
val exerciseId: Long,
49+
val exerciseName: String,
50+
val imageUrl: String?,
51+
)
52+
53+
@JsonClass(generateAdapter = true)
54+
data class PostPreferredExerciseDTO(
55+
val exerciseTypeId: Long,
56+
val skillLevel: String,
57+
val daysOfWeek: List<Boolean>,
58+
)
59+
60+
@JsonClass(generateAdapter = true)
61+
data class DeletePreferredExerciseDTO(
62+
val preferredExerciseIds: List<Long>,
3463
)
3564

3665
@JsonClass(generateAdapter = true)

data/src/main/java/com/project200/data/impl/MemberRepositoryImpl.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,24 @@ import android.content.Context
44
import androidx.core.net.toUri
55
import com.project200.common.di.IoDispatcher
66
import com.project200.data.api.ApiService
7+
import com.project200.data.dto.DeletePreferredExerciseDTO
78
import com.project200.data.dto.GetBlockedMemberDTO
89
import com.project200.data.dto.GetOpenChatUrlDTO
10+
import com.project200.data.dto.GetPreferredExerciseDTO
11+
import com.project200.data.dto.GetPreferredExerciseTypeDTO
912
import com.project200.data.dto.GetProfileDTO
1013
import com.project200.data.dto.GetProfileImageResponseDto
1114
import com.project200.data.dto.GetScoreDTO
1215
import com.project200.data.dto.PutProfileRequest
1316
import com.project200.data.mapper.toModel
1417
import com.project200.data.mapper.toMultipartBodyPart
18+
import com.project200.data.mapper.toPostDto
1519
import com.project200.data.utils.apiCallBuilder
1620
import com.project200.domain.model.BaseResult
1721
import com.project200.domain.model.BlockedMember
22+
import com.project200.domain.model.ExerciseType
1823
import com.project200.domain.model.OpenUrl
24+
import com.project200.domain.model.PreferredExercise
1925
import com.project200.domain.model.ProfileImageList
2026
import com.project200.domain.model.Score
2127
import com.project200.domain.model.UserProfile
@@ -158,6 +164,50 @@ class MemberRepositoryImpl
158164
)
159165
}
160166

167+
override suspend fun getPreferredExercises(): BaseResult<List<PreferredExercise>> {
168+
return apiCallBuilder(
169+
ioDispatcher = ioDispatcher,
170+
apiCall = { apiService.getPreferredExercises() },
171+
mapper = { dtoList: List<GetPreferredExerciseDTO>? ->
172+
dtoList?.map { it.toModel() } ?: throw NoSuchElementException()
173+
},
174+
)
175+
}
176+
177+
override suspend fun getPreferredExerciseTypes(): BaseResult<List<ExerciseType>> {
178+
return apiCallBuilder(
179+
ioDispatcher = ioDispatcher,
180+
apiCall = { apiService.getPreferredExerciseTypes() },
181+
mapper = { dtoList: List<GetPreferredExerciseTypeDTO>? ->
182+
dtoList?.map { it.toModel() } ?: throw NoSuchElementException()
183+
},
184+
)
185+
}
186+
187+
override suspend fun createPreferredExercise(preferredExercises: List<PreferredExercise>): BaseResult<Unit> {
188+
return apiCallBuilder(
189+
ioDispatcher = ioDispatcher,
190+
apiCall = { apiService.postPreferredExercises(preferredExercises.map { it.toPostDto() }) },
191+
mapper = { Unit },
192+
)
193+
}
194+
195+
override suspend fun editPreferredExercise(preferredExercises: List<PreferredExercise>): BaseResult<Unit> {
196+
return apiCallBuilder(
197+
ioDispatcher = ioDispatcher,
198+
apiCall = { apiService.patchPreferredExercises(preferredExercises.map { it.toPostDto() }) },
199+
mapper = { Unit },
200+
)
201+
}
202+
203+
override suspend fun deletePreferredExercise(preferredExerciseIds: List<Long>): BaseResult<Unit> {
204+
return apiCallBuilder(
205+
ioDispatcher = ioDispatcher,
206+
apiCall = { apiService.deletePreferredExercises(DeletePreferredExerciseDTO(preferredExerciseIds)) },
207+
mapper = { Unit },
208+
)
209+
}
210+
161211
companion object {
162212
const val IMAGE_PART_ERROR = "IMAGE_PART_ERROR"
163213
}

data/src/main/java/com/project200/data/mapper/MatchingMapper.kt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import com.project200.domain.model.ExercisePlace
99
import com.project200.domain.model.Location
1010
import com.project200.domain.model.MatchingMember
1111
import com.project200.domain.model.MatchingMemberProfile
12-
import com.project200.domain.model.PreferredExercise
1312

1413
fun GetMatchingMembersDto.toModel(): MatchingMember {
1514
return MatchingMember(
@@ -43,15 +42,7 @@ fun GetMatchingProfileDTO.toModel(): MatchingMemberProfile {
4342
exerciseCountInLast30Days = this.exerciseCountInLast30Days,
4443
exerciseScore = this.exerciseScore,
4544
preferredExercises =
46-
this.preferredExercises.map {
47-
PreferredExercise(
48-
preferredExerciseId = it.preferredExerciseId,
49-
name = it.name,
50-
skillLevel = it.skillLevel,
51-
daysOfWeek = it.daysOfWeek,
52-
imageUrl = it.imageUrl,
53-
)
54-
},
45+
this.preferredExercises.map { it.toModel() },
5546
)
5647
}
5748

0 commit comments

Comments
 (0)