Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,3 @@ import kotlinx.serialization.Serializable

@Serializable
data object AppMain : Route // 전체 앱의 메인 (기존 MainScreen)

@Serializable
data object Onboarding : Route
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.hsLink.hslink.data.di

import com.hsLink.hslink.data.remote.datasource.CommunityPostDataSource
import com.hsLink.hslink.data.remote.datasource.OnboardingDataSource
import com.hsLink.hslink.data.remote.datasource.PostDataSource
import com.hsLink.hslink.data.remote.datasource.SearchDataSource
import com.hsLink.hslink.data.remote.datasourceimpl.CommunityPostDataSourceImpl
import com.hsLink.hslink.data.remote.datasourceimpl.OnboardingDataSourceImpl
import com.hsLink.hslink.data.remote.datasourceimpl.PostDataSourceImpl
import com.hsLink.hslink.data.remote.datasourceimpl.SearchDataSourceImpl
import dagger.Binds
Expand All @@ -28,4 +30,11 @@ interface DataSourceModule {
abstract fun bindsSearchDataSource(
searchDataSourceImpl: SearchDataSourceImpl,
): SearchDataSource


@Binds
abstract fun bindsOnboardingDataSource(
onboardingDataSourceImpl: OnboardingDataSourceImpl
): OnboardingDataSource

}
7 changes: 7 additions & 0 deletions app/src/main/java/com/hsLink/hslink/data/di/NetworkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.hsLink.hslink.data.remote.AuthInterceptor
import com.hsLink.hslink.data.service.commuunity.CommunityPostService
import com.hsLink.hslink.data.service.home.PostService
import com.hsLink.hslink.data.service.login.AuthService
import com.hsLink.hslink.data.service.onboarding.OnboardingService
import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import dagger.Module
import dagger.Provides
Expand Down Expand Up @@ -69,4 +70,10 @@ object NetworkModule {
fun provideAuthService(retrofit: Retrofit): AuthService {
return retrofit.create(AuthService::class.java)
}

@Provides
@Singleton
fun provideOnboardingService(retrofit: Retrofit): OnboardingService {
return retrofit.create(OnboardingService::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import com.hsLink.hslink.data.repositoryimpl.AuthRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.CommunityRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.DummyRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.home.PostRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.onboarding.OnboardingRepositoryImpl
import com.hsLink.hslink.data.repositoryimpl.search.SearchRepositoryImpl
import com.hsLink.hslink.domain.DummyRepository
import com.hsLink.hslink.domain.repository.AuthRepository
import com.hsLink.hslink.domain.repository.community.CommunityRepository
import com.hsLink.hslink.domain.repository.home.PostRepository
import com.hsLink.hslink.domain.repository.onboarding.OnboardingRepository
import com.hsLink.hslink.domain.repository.search.SearchRepository
import dagger.Binds
import dagger.Module
Expand Down Expand Up @@ -45,4 +47,9 @@ interface RepositoryModule {
fun bindsSearchRepository(
searchRepositoryImpl: SearchRepositoryImpl,
): SearchRepository

@Binds
fun bindsOnboardingRepository(
onboardingRepositoryImpl: OnboardingRepositoryImpl,
): OnboardingRepository
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.hsLink.hslink.data.dto.request.onboarding

import com.hsLink.hslink.presentation.onboarding.model.JobType
import com.hsLink.hslink.presentation.onboarding.model.LinkType
Comment on lines +3 to +4
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix layering violation: Data layer should not import presentation layer types.

The data layer (DTOs) is importing JobType and LinkType from the presentation layer (com.hsLink.hslink.presentation.onboarding.model). This violates clean architecture principles where data/domain layers should not depend on the presentation layer.

Move JobType and LinkType to the domain layer (e.g., com.hsLink.hslink.domain.model.onboarding) so they can be shared across layers without creating circular dependencies or layering violations.

-import com.hsLink.hslink.presentation.onboarding.model.JobType
-import com.hsLink.hslink.presentation.onboarding.model.LinkType
+import com.hsLink.hslink.domain.model.onboarding.JobType
+import com.hsLink.hslink.domain.model.onboarding.LinkType

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
app/src/main/java/com/hsLink/hslink/data/dto/request/onboarding/CareerRequest.kt
around lines 3-4, the DTO is importing presentation-layer types JobType and
LinkType which breaks layering; move those types to the domain layer (suggested
package: com.hsLink.hslink.domain.model.onboarding), update their package
declarations, replace imports in CareerRequest.kt to reference the new domain
package, and update any other files that currently import the presentation
versions to use the domain types; rebuild and run tests to ensure no remaining
references to the old presentation package.

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CareerRequest(
@SerialName("companyName")
val companyName: String,
@SerialName("position")
val position: String,
@SerialName("jobType")
val jobType: JobType,
@SerialName("startYm")
val startYm: String,
@SerialName("endYm")
val endYm: String? = null,
@SerialName("employed")
val employed: Boolean
)

@Serializable
data class LinkRequest(
@SerialName("type")
val type: LinkType,
@SerialName("url")
val url: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.hsLink.hslink.data.dto.request.onboarding

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class OnboardingRequest (
@SerialName("name")
val name: String,
@SerialName("major")
val major: String,
@SerialName("studentNumber")
val studentNumber: String,
@SerialName("jobSeeking")
val jobSeeking : Boolean,
@SerialName("mentor")
val mentor : Boolean,
@SerialName("email")
val email : String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.hsLink.hslink.data.dto.response.onboarding

import com.hsLink.hslink.domain.model.search.CareerItemEntity
import com.hsLink.hslink.domain.model.search.LinkItemEntity
import com.hsLink.hslink.presentation.onboarding.model.JobType
import com.hsLink.hslink.presentation.onboarding.model.LinkType
Comment on lines +5 to +6
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Architecture violation: data layer depends on presentation layer.

The DTO is importing JobType and LinkType from the presentation package, which violates clean architecture principles. The data layer should not depend on the presentation layer.

Consider one of these solutions:

  1. Recommended: Move JobType and LinkType enums to the domain layer where they logically belong as business entities.
  2. Use String in the DTOs and map to the domain enums in the toEntity() functions.

Apply this approach if moving enums to domain:

-import com.hsLink.hslink.presentation.onboarding.model.JobType
-import com.hsLink.hslink.presentation.onboarding.model.LinkType
+import com.hsLink.hslink.domain.model.JobType
+import com.hsLink.hslink.domain.model.LinkType

Then move the enum definitions from presentation/onboarding/model/OnboardingState.kt to the domain layer.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt
around lines 5-6, the DTO imports JobType and LinkType from the presentation
layer which violates clean architecture; move the JobType and LinkType enum
definitions from presentation/onboarding/model/OnboardingState.kt into the
domain layer (e.g., app/src/main/java/com/hsLink/hslink/domain/model/) and
update their package declarations, then change the imports in CareerResponse to
reference the new domain package and adjust any toEntity()/mapping code to use
the domain enums; alternatively, if you prefer not to move enums, change the DTO
fields to String and perform String→enum mapping in the mapper/toEntity
functions in the data layer.

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class CareerResponse(
@SerialName("id") val id: Int,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify ID type inconsistency.

CareerResponse.id uses Int (Line 12) while LinkResponse.id uses Long (Line 23). This inconsistency might be intentional based on your backend API design, but please verify that these types match the actual API response types.

Run this script to check if there are API documentation or integration tests that confirm the ID types:

Also applies to: 23-23


🏁 Script executed:

#!/bin/bash
# Search for API endpoint definitions or documentation mentioning career and link IDs
rg -n -C3 -g '!*.kt' -e 'career.*id|link.*id' -i

Length of output: 333


🏁 Script executed:

#!/bin/bash
# Get full content of CareerResponse and LinkResponse DTOs
echo "=== CareerResponse.kt ==="
cat app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt

echo -e "\n=== LinkResponse.kt ==="
cat app/src/main/java/com/hsLink/hslink/data/dto/response/LinkResponse.kt

Length of output: 1549


🏁 Script executed:

#!/bin/bash
# Search for Retrofit API interface definitions
fd -e 'kt' -p '.*[Aa]pi.*' --type f | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for where CareerResponse and LinkResponse are deserialized or used
rg -n -C2 'CareerResponse|LinkResponse' --type=kt -g '!test'

Length of output: 26


🏁 Script executed:

#!/bin/bash
# Search for CareerItemEntity and LinkItemEntity to see their ID types
rg -n 'data class CareerItemEntity|data class LinkItemEntity' --type rust

Length of output: 148


🏁 Script executed:

#!/bin/bash
# Search for API service interface definitions (alternative syntax for Kotlin)
fd 'Service\|Api\|Repository' -e 'kt' | head -20

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for usages of CareerResponse and LinkResponse to understand context
rg -n -C3 'CareerResponse\|LinkResponse'

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Look for test files or fixtures showing API response structure
fd -e 'json' -o -e 'kt' | xargs rg -l 'career.*id\|link.*id' -i | head -10

Length of output: 116


🏁 Script executed:

#!/bin/bash
# Simple search for entity classes without type filters
rg -n 'data class CareerItemEntity' --no-heading

Length of output: 108


🏁 Script executed:

#!/bin/bash
# Search for any API or Service Kotlin files
find . -name '*Api.kt' -o -name '*Service.kt' -o -name '*Repository.kt' 2>/dev/null | head -15

Length of output: 933


🏁 Script executed:

#!/bin/bash
# Look at CareerResponse file again and grep for imports/usages
grep -r 'CareerResponse\|LinkResponse' . --include='*.kt' 2>/dev/null | head -20

Length of output: 2197


🏁 Script executed:

#!/bin/bash
# Check build.gradle for serialization library and backend info
find . -name 'build.gradle*' -o -name 'settings.gradle*' | xargs grep -l 'serialization\|retrofit\|okhttp' 2>/dev/null

Length of output: 41


🏁 Script executed:

#!/bin/bash
# Get CareerItemEntity and LinkItemEntity definitions
cat app/src/main/java/com/hsLink/hslink/domain/model/search/UserProfileEntity.kt

Length of output: 1074


🏁 Script executed:

#!/bin/bash
# Get OnboardingService API interface to see endpoint signatures
cat app/src/main/java/com/hsLink/hslink/data/service/onboarding/OnboardingService.kt

Length of output: 764


🏁 Script executed:

#!/bin/bash
# Check if there are any comments or documentation about ID types
grep -r 'id.*Int\|id.*Long' app/src/main/java/com/hsLink/hslink/domain/model/search/ --include='*.kt' -B2 -A2

Length of output: 1923


Resolve inconsistency between CareerItemEntity and CareerEntity ID types.

While CareerResponse.id (Int) and LinkResponse.id (Long) correctly map to their respective entity types, a domain-level inconsistency exists: CareerItemEntity.id is Int while CareerEntity.id is Long. Both represent career data but have different ID types. Verify this is intentional or align them for consistency across the domain model.

🤖 Prompt for AI Agents
In app/src/main/java/com/hsLink/hslink/data/dto/response/CareerResponse.kt
around line 12, CareerResponse.id is declared as Int while the domain has
CareerEntity.id as Long and CareerItemEntity.id as Int; verify intended ID type
and make the model consistent: either change CareerResponse.id to Long to match
CareerEntity (and update LinkResponse if needed), or change CareerEntity.id to
Int (and update persistence layer and migrations) so all career-related
DTOs/entities use the same ID type across the domain; update all mapping code,
constructors, serializers, and any database/ORM types accordingly to avoid type
mismatches.

@SerialName("companyName") val companyName: String,
@SerialName("position") val position: String,
@SerialName("jobType") val jobType: JobType,
@SerialName("employed") val employed: Boolean,
@SerialName("startYm") val startYm: String,
@SerialName("endYm") val endYm: String?,
)

@Serializable
data class LinkResponse(
@SerialName("id") val id: Long,
@SerialName("type") val type: LinkType,
@SerialName("url") val url: String,
)

typealias CareerListResponseDto = List<CareerResponse>

@Serializable
data class LinkListResponseDto(
@SerialName("links") val links: List<LinkResponse>,
)

fun CareerResponse.toEntity(): CareerItemEntity = CareerItemEntity(
id = id, companyName = companyName, position = position,
jobType = jobType, employed = employed, startYm = startYm, endYm = endYm
)

fun LinkResponse.toEntity(): LinkItemEntity = LinkItemEntity(
id = id, type = type, url = url
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.hsLink.hslink.data.remote.datasource

import com.hsLink.hslink.core.network.BaseResponse
import com.hsLink.hslink.data.dto.request.onboarding.*
import com.hsLink.hslink.data.dto.response.onboarding.*

interface OnboardingDataSource {
suspend fun submitOnboarding(request: OnboardingRequest): BaseResponse<Unit>
suspend fun getCareers(): BaseResponse<List<CareerResponse>>
suspend fun submitCareer(request: CareerRequest): BaseResponse<CareerResponse>
suspend fun getLinks(): BaseResponse<LinkListResponseDto>
suspend fun submitLink(request: LinkRequest): LinkResponse
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.hsLink.hslink.data.remote.datasourceimpl

import com.hsLink.hslink.core.network.BaseResponse
import com.hsLink.hslink.data.dto.request.onboarding.*
import com.hsLink.hslink.data.remote.datasource.OnboardingDataSource
import com.hsLink.hslink.data.service.onboarding.OnboardingService
import com.hsLink.hslink.data.dto.response.onboarding.*
import javax.inject.Inject

class OnboardingDataSourceImpl @Inject constructor(
private val onboardingService: OnboardingService
) : OnboardingDataSource {
override suspend fun submitOnboarding(request: OnboardingRequest): BaseResponse<Unit> =
onboardingService.onboarding(request)

override suspend fun getCareers(): BaseResponse<List<CareerResponse>> =
onboardingService.getCareers()

override suspend fun submitCareer(request: CareerRequest): BaseResponse<CareerResponse> =
onboardingService.submitCareer(request)

override suspend fun getLinks(): BaseResponse<LinkListResponseDto> =
onboardingService.getLinks()

override suspend fun submitLink(request: LinkRequest): LinkResponse =
onboardingService.submitLink(request)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.hsLink.hslink.data.repositoryimpl.onboarding

import com.hsLink.hslink.data.dto.request.onboarding.*
import com.hsLink.hslink.data.dto.response.onboarding.toEntity
import com.hsLink.hslink.data.remote.datasource.OnboardingDataSource
import com.hsLink.hslink.domain.model.search.*
import com.hsLink.hslink.domain.repository.onboarding.OnboardingRepository
import javax.inject.Inject

class OnboardingRepositoryImpl @Inject constructor(
private val onboardingDataSource: OnboardingDataSource,
) : OnboardingRepository {
override suspend fun submitOnboarding(request: OnboardingRequest): Result<Unit> = runCatching {
val response = onboardingDataSource.submitOnboarding(request)
if (response.isSuccess) response.result
else throw Exception(response.message)
}

override suspend fun getCareers(): Result<List<CareerItemEntity>> = runCatching {
val response = onboardingDataSource.getCareers()
if (response.isSuccess && response.result != null) {
response.result.map { it.toEntity() }
} else throw Exception(response.message ?: "Failed to get careers")
}

override suspend fun submitCareer(request: CareerRequest): Result<CareerItemEntity> = runCatching {
val response = onboardingDataSource.submitCareer(request)
if (response.isSuccess && response.result != null) {
response.result.toEntity()
} else throw Exception(response.message ?: "Failed to submit career")
}

override suspend fun getLinks(): Result<List<LinkItemEntity>> = runCatching {
val response = onboardingDataSource.getLinks()
if (response.isSuccess && response.result != null) {
response.result.links.map { it.toEntity() }
} else throw Exception(response.message ?: "Failed to get links")
}

override suspend fun submitLink(request: LinkRequest): Result<LinkItemEntity> = runCatching {
onboardingDataSource.submitLink(request).toEntity()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.hsLink.hslink.data.service.onboarding

import com.hsLink.hslink.core.network.BaseResponse
import com.hsLink.hslink.data.dto.request.onboarding.*
import com.hsLink.hslink.data.dto.response.onboarding.*
import retrofit2.http.*

interface OnboardingService {
@POST("auth/onboarding")
suspend fun onboarding(@Body request: OnboardingRequest): BaseResponse<Unit>

@GET("careers/mycareers")
suspend fun getCareers(): BaseResponse<List<CareerResponse>>

@POST("careers")
suspend fun submitCareer(@Body request: CareerRequest): BaseResponse<CareerResponse>

@GET("links/mylinks")
suspend fun getLinks(): BaseResponse<LinkListResponseDto>

@POST("links")
suspend fun submitLink(@Body request: LinkRequest): LinkResponse
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.hsLink.hslink.domain.model.search

import com.hsLink.hslink.presentation.onboarding.model.JobType
import com.hsLink.hslink.presentation.onboarding.model.LinkType
Comment on lines +3 to +4
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix layering violation: Domain layer should not import presentation layer types.

The domain layer is importing JobType and LinkType from the presentation layer. Domain entities should not depend on presentation layer types to maintain clean architecture boundaries.

Move JobType and LinkType to the domain layer so they can be properly shared without violating layering principles:

-import com.hsLink.hslink.presentation.onboarding.model.JobType
-import com.hsLink.hslink.presentation.onboarding.model.LinkType
+import com.hsLink.hslink.domain.model.onboarding.JobType
+import com.hsLink.hslink.domain.model.onboarding.LinkType

Committable suggestion skipped: line range outside the PR's diff.


data class UserProfileEntity(
val userId: Long,
val name: String,
Expand All @@ -13,6 +16,16 @@ data class UserProfileEntity(
val links: List<LinkEntity>
)

data class CareerItemEntity(
val id: Int,
val companyName: String,
val position: String,
val jobType: JobType,
val employed: Boolean,
val startYm: String,
val endYm: String?
)

data class CareerEntity(
val id: Long,
val companyName: String,
Expand All @@ -27,4 +40,10 @@ data class LinkEntity(
val id: Long,
val type: String,
val url: String
)

data class LinkItemEntity(
val id: Long,
val type: LinkType,
val url: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.hsLink.hslink.domain.repository.onboarding

import com.hsLink.hslink.data.dto.request.onboarding.CareerRequest
import com.hsLink.hslink.data.dto.request.onboarding.LinkRequest
import com.hsLink.hslink.data.dto.request.onboarding.OnboardingRequest
import com.hsLink.hslink.domain.model.search.CareerItemEntity
import com.hsLink.hslink.domain.model.search.LinkItemEntity

interface OnboardingRepository {
suspend fun submitOnboarding(request: OnboardingRequest): Result<Unit>
suspend fun getCareers(): Result<List<CareerItemEntity>>
suspend fun submitCareer(request: CareerRequest): Result<CareerItemEntity>
suspend fun getLinks(): Result<List<LinkItemEntity>>
suspend fun submitLink(request: LinkRequest): Result<LinkItemEntity>
}
Loading