feat(creator): 채널 홈 API 모델을 추가한다

This commit is contained in:
2026-06-13 16:21:53 +09:00
parent 80e8213f12
commit 0934a7ec51
3 changed files with 241 additions and 0 deletions

View File

@@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.v2.creator.channel.data
import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.common.ApiResponse
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Path
interface CreatorChannelHomeApi {
@GET("/api/v2/creator-channels/{creatorId}/home")
fun getHome(
@Path("creatorId") creatorId: Long,
@Header("Authorization") authHeader: String
): Single<ApiResponse<CreatorChannelHomeResponse>>
}

View File

@@ -0,0 +1,144 @@
package kr.co.vividnext.sodalive.v2.creator.channel.data
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
@Keep
data class CreatorChannelHomeResponse(
@SerializedName("creator") val creator: CreatorChannelCreatorResponse,
@SerializedName("currentLive") val currentLive: CreatorChannelLiveResponse?,
@SerializedName("latestAudioContent") val latestAudioContent: CreatorChannelAudioContentResponse?,
@SerializedName("channelDonations") val channelDonations: List<CreatorChannelDonationResponse>,
@SerializedName("notices") val notices: List<CreatorChannelCommunityPostResponse>,
@SerializedName("schedules") val schedules: List<CreatorChannelScheduleResponse>,
@SerializedName("audioContents") val audioContents: List<CreatorChannelAudioContentResponse>,
@SerializedName("series") val series: List<CreatorChannelSeriesResponse>,
@SerializedName("communities") val communities: List<CreatorChannelCommunityPostResponse>,
@SerializedName("fanTalk") val fanTalk: CreatorChannelFanTalkSummaryResponse,
@SerializedName("introduce") val introduce: String,
@SerializedName("activity") val activity: CreatorChannelActivityResponse,
@SerializedName("sns") val sns: CreatorChannelSnsResponse
)
@Keep
data class CreatorChannelCreatorResponse(
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("characterId") val characterId: Long?,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("followerCount") val followerCount: Int,
@SerializedName("isAiChatAvailable") val isAiChatAvailable: Boolean,
@SerializedName("isDmAvailable") val isDmAvailable: Boolean,
@SerializedName("isFollow") val isFollow: Boolean,
@SerializedName("isNotify") val isNotify: Boolean
)
@Keep
data class CreatorChannelLiveResponse(
@SerializedName("liveId") val liveId: Long,
@SerializedName("title") val title: String,
@SerializedName("coverImageUrl") val coverImageUrl: String?,
@SerializedName("beginDateTimeUtc") val beginDateTimeUtc: String,
@SerializedName("price") val price: Int,
@SerializedName("isAdult") val isAdult: Boolean
)
@Keep
data class CreatorChannelAudioContentResponse(
@SerializedName("audioContentId") val audioContentId: Long,
@SerializedName("title") val title: String,
@SerializedName("duration") val duration: String?,
@SerializedName("imageUrl") val imageUrl: String?,
@SerializedName("price") val price: Int,
@SerializedName("isPointAvailable") val isPointAvailable: Boolean,
@SerializedName("isFirstContent") val isFirstContent: Boolean,
@SerializedName("seriesName") val seriesName: String?,
@SerializedName("isOriginalSeries") val isOriginalSeries: Boolean?
)
@Keep
data class CreatorChannelDonationResponse(
@SerializedName("donationId") val donationId: Long,
@SerializedName("memberId") val memberId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("can") val can: Int,
@SerializedName("isSecret") val isSecret: Boolean,
@SerializedName("message") val message: String,
@SerializedName("createdAtUtc") val createdAtUtc: String
)
@Keep
data class CreatorChannelScheduleResponse(
@SerializedName("scheduledAtUtc") val scheduledAtUtc: String,
@SerializedName("title") val title: String,
@SerializedName("type") val type: CreatorActivityType,
@SerializedName("targetId") val targetId: Long
)
@Keep
data class CreatorChannelSeriesResponse(
@SerializedName("seriesId") val seriesId: Long,
@SerializedName("title") val title: String,
@SerializedName("coverImageUrl") val coverImageUrl: String,
@SerializedName("publishedDaysOfWeek") val publishedDaysOfWeek: String,
@SerializedName("isComplete") val isComplete: Boolean,
@SerializedName("numberOfContent") val numberOfContent: Int,
@SerializedName("isNew") val isNew: Boolean,
@SerializedName("isPopular") val isPopular: Boolean,
@SerializedName("isOriginal") val isOriginal: Boolean
)
@Keep
data class CreatorChannelCommunityPostResponse(
@SerializedName("postId") val postId: Long,
@SerializedName("creatorId") val creatorId: Long,
@SerializedName("creatorNickname") val creatorNickname: String,
@SerializedName("creatorProfileUrl") val creatorProfileUrl: String,
@SerializedName("imageUrl") val imageUrl: String?,
@SerializedName("audioUrl") val audioUrl: String?,
@SerializedName("content") val content: String,
@SerializedName("price") val price: Int,
@SerializedName("dateUtc") val dateUtc: String,
@SerializedName("existOrdered") val existOrdered: Boolean,
@SerializedName("likeCount") val likeCount: Int,
@SerializedName("commentCount") val commentCount: Int
)
@Keep
data class CreatorChannelFanTalkSummaryResponse(
@SerializedName("totalCount") val totalCount: Int,
@SerializedName("latestFanTalk") val latestFanTalk: CreatorChannelFanTalkResponse?
)
@Keep
data class CreatorChannelFanTalkResponse(
@SerializedName("fanTalkId") val fanTalkId: Long,
@SerializedName("memberId") val memberId: Long,
@SerializedName("nickname") val nickname: String,
@SerializedName("profileImageUrl") val profileImageUrl: String,
@SerializedName("content") val content: String,
@SerializedName("languageCode") val languageCode: String?,
@SerializedName("createdAtUtc") val createdAtUtc: String
)
@Keep
data class CreatorChannelActivityResponse(
@SerializedName("debutDateUtc") val debutDateUtc: String?,
@SerializedName("dDay") val dDay: String,
@SerializedName("liveCount") val liveCount: Long,
@SerializedName("liveDurationHours") val liveDurationHours: Long,
@SerializedName("liveContributorCount") val liveContributorCount: Long,
@SerializedName("audioContentCount") val audioContentCount: Long,
@SerializedName("seriesCount") val seriesCount: Long
)
@Keep
data class CreatorChannelSnsResponse(
@SerializedName("instagramUrl") val instagramUrl: String,
@SerializedName("fancimmUrl") val fancimmUrl: String,
@SerializedName("xUrl") val xUrl: String,
@SerializedName("youtubeUrl") val youtubeUrl: String,
@SerializedName("kakaoOpenChatUrl") val kakaoOpenChatUrl: String
)

View File

@@ -0,0 +1,82 @@
package kr.co.vividnext.sodalive.v2.creator.channel
import com.google.gson.Gson
import kr.co.vividnext.sodalive.v2.common.CreatorActivityType
import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelHomeResponse
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
class CreatorChannelHomeModelsTest {
private val gson = Gson()
@Test
fun `home response parses schedule type to creator activity type`() {
val response = gson.fromJson(homeJson(scheduleType = "LIVE_REPLAY"), CreatorChannelHomeResponse::class.java)
assertEquals(CreatorActivityType.LiveReplay, response.schedules.single().type)
}
@Test
fun `home response preserves nullable fields`() {
val response = gson.fromJson(homeJson(scheduleType = "AUDIO"), CreatorChannelHomeResponse::class.java)
assertNull(response.creator.characterId)
assertNull(response.currentLive)
assertNull(response.latestAudioContent)
assertNull(response.fanTalk.latestFanTalk)
assertNull(response.activity.debutDateUtc)
}
private fun homeJson(scheduleType: String): String = """
{
"creator": {
"creatorId": 1,
"characterId": null,
"nickname": "creator",
"profileImageUrl": "https://example.com/profile.png",
"followerCount": 10,
"isAiChatAvailable": true,
"isDmAvailable": true,
"isFollow": false,
"isNotify": false
},
"currentLive": null,
"latestAudioContent": null,
"channelDonations": [],
"notices": [],
"schedules": [
{
"scheduledAtUtc": "2026-06-12T00:00:00Z",
"title": "schedule",
"type": "$scheduleType",
"targetId": 11
}
],
"audioContents": [],
"series": [],
"communities": [],
"fanTalk": {
"totalCount": 0,
"latestFanTalk": null
},
"introduce": "intro",
"activity": {
"debutDateUtc": null,
"dDay": "D+1",
"liveCount": 1,
"liveDurationHours": 2,
"liveContributorCount": 3,
"audioContentCount": 4,
"seriesCount": 5
},
"sns": {
"instagramUrl": "",
"fancimmUrl": "",
"xUrl": "",
"youtubeUrl": "",
"kakaoOpenChatUrl": ""
}
}
""".trimIndent()
}