diff --git a/core/common/src/test/java/com/android/mediproject/common/core/XMLDummyData.kt b/core/common/src/test/java/com/android/mediproject/common/core/XMLDummyData.kt index d01bbb2f0..d0cb4569c 100644 --- a/core/common/src/test/java/com/android/mediproject/common/core/XMLDummyData.kt +++ b/core/common/src/test/java/com/android/mediproject/common/core/XMLDummyData.kt @@ -571,11 +571,41 @@ val ubDocData = """ """.trimIndent() val eeDocData = """ - +
- - 2 이상의 비만환자 또는 다른 위험인자(예. 제2형 당뇨, 이상지질혈증, 고혈압)가 있는 체질량지수(BMI) 27kg/m2 이상 30kg/m2 미만인 과체중 환자의 체중조절을 위한 식이 및 운동요법의 보조요법]]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/core/model/src/main/java/com/android/mediproject/core/model/local/navargs/BaseNavArgs.kt b/core/model/src/main/java/com/android/mediproject/core/model/local/navargs/BaseNavArgs.kt index 85b2d9303..53451f45c 100644 --- a/core/model/src/main/java/com/android/mediproject/core/model/local/navargs/BaseNavArgs.kt +++ b/core/model/src/main/java/com/android/mediproject/core/model/local/navargs/BaseNavArgs.kt @@ -10,7 +10,8 @@ import kotlin.reflect.full.primaryConstructor import kotlin.reflect.full.starProjectedType abstract class BaseNavArgs( - val className: String) : NavArgs { + val className: String, +) : NavArgs { fun toBundle(): Bundle { return bundleOf(*toMap().toList().toTypedArray()) } @@ -137,6 +138,8 @@ abstract class BaseNavArgs( // 속성 값이 NULL이면 빈 값을 넣어준다. fun toMap(): Map = this::class.memberProperties.associate { property -> - property.name to (property.getter.call(this) ?: emptyValue(property.returnType)) + property.name to (property.getter.call(this).run { + if (this is String) replace("%", "%25") else this + } ?: emptyValue(property.returnType)) } -} \ No newline at end of file +} diff --git a/core/model/src/main/java/com/android/mediproject/core/model/medicine/medicinedetailinfo/MedicineDetailInfoResponse.kt b/core/model/src/main/java/com/android/mediproject/core/model/medicine/medicinedetailinfo/MedicineDetailInfoResponse.kt index dbe08ca6b..7945fdeb5 100644 --- a/core/model/src/main/java/com/android/mediproject/core/model/medicine/medicinedetailinfo/MedicineDetailInfoResponse.kt +++ b/core/model/src/main/java/com/android/mediproject/core/model/medicine/medicinedetailinfo/MedicineDetailInfoResponse.kt @@ -69,48 +69,48 @@ data class MedicineDetailInfoResponse( */ @Serializable data class Item( - @SerialName("ATC_CODE") val atcCode: String?, - @SerialName("BAR_CODE") val barCode: String?, - @SerialName("BIZRNO") val businessRegistrationNumber: String?, - @SerialName("CANCEL_DATE") val cancelDate: String?, - @SerialName("CANCEL_NAME") val cancelName: String?, - @SerialName("CHANGE_DATE") val changeDate: String, - @SerialName("CHART") val chart: String?, - @SerialName("CNSGN_MANUF") val consignmentManufacturer: String?, - @SerialName("DOC_TEXT") val docText: String?, - @SerialName("EDI_CODE") val ediCode: String?, - @SerialName("EE_DOC_DATA") val eeDocData: String, - @SerialName("EE_DOC_ID") val eeDocId: String?, - @SerialName("ENTP_ENG_NAME") val entpEnglishName: String?, - @SerialName("ENTP_NAME") val entpName: String, - @SerialName("ENTP_NO") val entpNumber: String?, - @SerialName("ETC_OTC_CODE") val etcOtcCode: String, - @SerialName("GBN_NAME") val gbnName: String?, - @SerialName("INDUTY_TYPE") val industryType: String?, - @SerialName("INGR_NAME") val ingredientName: String, - @SerialName("INSERT_FILE") val insertFileUrl: String?, - @SerialName("ITEM_ENG_NAME") val itemEnglishName: String, - @SerialName("ITEM_NAME") val itemName: String, - @SerialName("ITEM_PERMIT_DATE") val itemPermitDate: String, - @SerialName("ITEM_SEQ") val itemSequence: String, - @SerialName("MAIN_INGR_ENG") val mainIngredientEnglish: String?, - @SerialName("MAIN_ITEM_INGR") val mainItemIngredient: String, - @SerialName("MAKE_MATERIAL_FLAG") val makeMaterialFlag: String?, - @SerialName("MATERIAL_NAME") val materialName: String?, - @SerialName("NARCOTIC_KIND_CODE") val narcoticKindCode: String?, - @SerialName("NB_DOC_DATA") val nbDocData: String, - @SerialName("NB_DOC_ID") val nbDocId: String?, - @SerialName("NEWDRUG_CLASS_NAME") val newDrugClassName: String?, - @SerialName("PACK_UNIT") val packUnit: String?, - @SerialName("PERMIT_KIND_NAME") val permitKindName: String?, - @SerialName("PN_DOC_DATA") val pnDocData: String?, - @SerialName("REEXAM_DATE") val reexamDate: String?, - @SerialName("REEXAM_TARGET") val reexamTarget: String?, - @SerialName("STORAGE_METHOD") val storageMethod: String?, - @SerialName("TOTAL_CONTENT") val totalContent: String?, - @SerialName("UD_DOC_DATA") val udDocData: String, - @SerialName("UD_DOC_ID") val uDDOCID: String?, - @SerialName("VALID_TERM") val validTerm: String?, + @SerialName("ATC_CODE") val atcCode: String = "", + @SerialName("BAR_CODE") val barCode: String = "", + @SerialName("BIZRNO") val businessRegistrationNumber: String = "", + @SerialName("CANCEL_DATE") val cancelDate: String = "", + @SerialName("CANCEL_NAME") val cancelName: String = "", + @SerialName("CHANGE_DATE") val changeDate: String = "", + @SerialName("CHART") val chart: String = "", + @SerialName("CNSGN_MANUF") val consignmentManufacturer: String = "", + @SerialName("DOC_TEXT") val docText: String = "", + @SerialName("EDI_CODE") val ediCode: String = "", + @SerialName("EE_DOC_DATA") val eeDocData: String = "", + @SerialName("EE_DOC_ID") val eeDocId: String = "", + @SerialName("ENTP_ENG_NAME") val entpEnglishName: String = "", + @SerialName("ENTP_NAME") val entpName: String = "", + @SerialName("ENTP_NO") val entpNumber: String = "", + @SerialName("ETC_OTC_CODE") val etcOtcCode: String = "", + @SerialName("GBN_NAME") val gbnName: String = "", + @SerialName("INDUTY_TYPE") val industryType: String = "", + @SerialName("INGR_NAME") val ingredientName: String = "", + @SerialName("INSERT_FILE") val insertFileUrl: String = "", + @SerialName("ITEM_ENG_NAME") val itemEnglishName: String = "", + @SerialName("ITEM_NAME") val itemName: String = "", + @SerialName("ITEM_PERMIT_DATE") val itemPermitDate: String = "", + @SerialName("ITEM_SEQ") val itemSequence: String = "", + @SerialName("MAIN_INGR_ENG") val mainIngredientEnglish: String = "", + @SerialName("MAIN_ITEM_INGR") val mainItemIngredient: String = "", + @SerialName("MAKE_MATERIAL_FLAG") val makeMaterialFlag: String = "", + @SerialName("MATERIAL_NAME") val materialName: String = "", + @SerialName("NARCOTIC_KIND_CODE") val narcoticKindCode: String = "", + @SerialName("NB_DOC_DATA") val nbDocData: String = "", + @SerialName("NB_DOC_ID") val nbDocId: String = "", + @SerialName("NEWDRUG_CLASS_NAME") val newDrugClassName: String = "", + @SerialName("PACK_UNIT") val packUnit: String = "", + @SerialName("PERMIT_KIND_NAME") val permitKindName: String = "", + @SerialName("PN_DOC_DATA") val pnDocData: String = "", + @SerialName("REEXAM_DATE") val reexamDate: String = "", + @SerialName("REEXAM_TARGET") val reexamTarget: String = "", + @SerialName("STORAGE_METHOD") val storageMethod: String = "", + @SerialName("TOTAL_CONTENT") val totalContent: String = "", + @SerialName("UD_DOC_DATA") val udDocData: String = "", + @SerialName("UD_DOC_ID") val uDDOCID: String = "", + @SerialName("VALID_TERM") val validTerm: String = "", ) } diff --git a/core/network/src/main/java/com/android/mediproject/core/network/ResponseWrapper.kt b/core/network/src/main/java/com/android/mediproject/core/network/ResponseWrapper.kt index b6f04cfdf..ab9290f74 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/ResponseWrapper.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/ResponseWrapper.kt @@ -1,5 +1,9 @@ package com.android.mediproject.core.network +import com.android.mediproject.core.model.DataGoKrBaseResponse +import com.android.mediproject.core.model.toResult +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.json.Json import retrofit2.Response /** @@ -9,7 +13,7 @@ import retrofit2.Response * * HTTP응답이 성공이면, 응답 객체(T)를 반환하고, 실패이면 실패 이유를 반환한다. */ -inline fun Response.onResponse(): Result { +internal inline fun Response.onResponse(): Result { return if (isSuccessful) { body()?.let { body -> Result.success(body) @@ -18,3 +22,36 @@ inline fun Response.onResponse(): Result { Result.failure(errorBody()?.string()?.let { Throwable(it) } ?: Throwable("Response Error")) } } + +private val json = Json { coerceInputValues = true } + +private inline fun String.parse(): Result = try { + Result.success(json.decodeFromString(this)) +} catch (e: Exception) { + Result.failure(e) +} + +internal inline fun Response.onStringResponse(): Result> { + return if (isSuccessful) { + body()?.parse()?.fold( + onSuccess = { final -> + final.toResult().fold( + onSuccess = { + Result.success(PairResponse(it, body()!!)) + }, + onFailure = { + Result.failure(it) + }, + ) + }, + onFailure = { Result.failure(it) }, + ) ?: Result.failure(Throwable("Response Body is Null")) + } else { + Result.failure(errorBody()?.string()?.let { Throwable(it) } ?: Throwable("Response Error")) + } +} + +internal data class PairResponse( + val first: T, + val second: E, +) diff --git a/core/network/src/main/java/com/android/mediproject/core/network/datasource/medicineapproval/MedicineApprovalDataSourceImpl.kt b/core/network/src/main/java/com/android/mediproject/core/network/datasource/medicineapproval/MedicineApprovalDataSourceImpl.kt index 43e0826b4..83ec7ae65 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/datasource/medicineapproval/MedicineApprovalDataSourceImpl.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/datasource/medicineapproval/MedicineApprovalDataSourceImpl.kt @@ -7,58 +7,53 @@ import com.android.mediproject.core.model.medicine.medicinedetailinfo.cache.Medi import com.android.mediproject.core.model.toResult import com.android.mediproject.core.network.datasource.image.GoogleSearchDataSource import com.android.mediproject.core.network.module.DataGoKrNetworkApi +import com.android.mediproject.core.network.module.safetyEncode import com.android.mediproject.core.network.onResponse -import kotlinx.coroutines.Dispatchers +import com.android.mediproject.core.network.onStringResponse +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.async import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.withContext -import kotlinx.serialization.encodeToString -import kotlinx.serialization.json.Json import java.lang.ref.WeakReference import javax.inject.Inject class MedicineApprovalDataSourceImpl @Inject constructor( - private val dataGoKrNetworkApi: DataGoKrNetworkApi, + private val dataGoKrNetworkApiWithString: DataGoKrNetworkApi, + private val dataGoKrNetworkApiWithJson: DataGoKrNetworkApi, private val medicineDataCacheManager: MedicineDataCacheManager, private val googleSearchDataSource: GoogleSearchDataSource, + private val defaultDispatcher: CoroutineDispatcher, ) : MedicineApprovalDataSource { override suspend fun getMedicineApprovalList( itemName: String?, entpName: String?, medicationType: String?, pageNo: Int, - ): Result = - dataGoKrNetworkApi.getApprovalList(itemName = itemName, entpName = entpName, pageNo = pageNo, medicationType = medicationType).onResponse() - .fold( - onSuccess = { response -> - response.toResult().fold( - onSuccess = { - // 이미지가 없는 경우 구글 검색을 통해 이미지를 가져온다. - loadMedicineImageUrl(response) - Result.success(response) - }, - onFailure = { - Result.failure(it) - }, - ) - }, - onFailure = { - Result.failure(it) - }, - ) + ): Result = dataGoKrNetworkApiWithJson.getApprovalList( + itemName = itemName?.safetyEncode(), entpName = entpName?.safetyEncode(), pageNo = pageNo, + medicationType = medicationType, + ).onResponse().fold( + onSuccess = { response -> + response.toResult().fold( + onSuccess = { + loadMedicineImageUrl(it) + Result.success(response) + }, + onFailure = { + Result.failure(it) + }, + ) + }, + onFailure = { + Result.failure(it) + }, + ) override fun getMedicineDetailInfo(itemName: String): Flow> = channelFlow { - dataGoKrNetworkApi.getMedicineDetailInfo(itemName = itemName).let { response -> - response.onResponse().fold( + dataGoKrNetworkApiWithString.getMedicineDetailInfo(itemName = itemName.safetyEncode()).let { response -> + response.onStringResponse().fold( onSuccess = { entity -> - entity.toResult().fold( - onSuccess = { - response.body()?.run { cache(this) } - Result.success(entity) - }, - onFailure = { - Result.failure(it) - }, - ) + cache(entity.first, entity.second) + Result.success(entity.first) }, onFailure = { Result.failure(it) @@ -71,18 +66,11 @@ class MedicineApprovalDataSourceImpl @Inject constructor( override fun getMedicineDetailInfoByItemSeq(itemSeqs: List) = channelFlow { val responses = itemSeqs.map { itemSeq -> - dataGoKrNetworkApi.getMedicineDetailInfo(itemSeq = itemSeq).let { response -> - response.onResponse().fold( + dataGoKrNetworkApiWithJson.getMedicineDetailInfo(itemSeq = itemSeq).let { response -> + response.onStringResponse().fold( onSuccess = { entity -> - entity.toResult().fold( - onSuccess = { - response.body()?.run { cache(this) } - Result.success(entity) - }, - onFailure = { - Result.failure(it) - }, - ) + cache(entity.first, entity.second) + Result.success(entity.first) }, onFailure = { Result.failure(it) @@ -91,29 +79,27 @@ class MedicineApprovalDataSourceImpl @Inject constructor( } } - val results = responses.let { - val failed = it.any { result -> result.isFailure } - if (failed) Result.failure(Throwable("약품 상세 정보 조회에 실패했습니다.")) - else Result.success(it.map { result -> result.getOrNull()!! }) + val result = responses.filter { it.isFailure }.run { + if (isEmpty()) Result.success(responses.map { it.getOrNull()!! }) + else Result.failure(first().exceptionOrNull()!!) } - trySend(results) + send(result) } - private fun cache(response: MedicineDetailInfoResponse) { - with(response.body.items[0]) { - medicineDataCacheManager.updateDetail( - MedicineCacheEntity( - itemSequence = itemSequence, - json = WeakReference(Json.encodeToString(response.body)).get()!!, - changeDate = changeDate, - ), - ) - } + private fun cache(response: MedicineDetailInfoResponse, string: String) { + val item = response.body.items.first() + medicineDataCacheManager.updateDetail( + MedicineCacheEntity( + itemSequence = item.itemSequence, + json = WeakReference(string).get()!!, + changeDate = item.changeDate, + ), + ) } private suspend fun loadMedicineImageUrl(medicineApprovalListResponse: MedicineApprovalListResponse) { - return withContext(Dispatchers.Default) { + return withContext(defaultDispatcher) { val items = mutableListOf>() medicineApprovalListResponse.body.items.forEachIndexed { index, item -> if (item.bigPrdtImgUrl.isEmpty()) items.add(index to item.itemName) diff --git a/core/network/src/main/java/com/android/mediproject/core/network/module/DataGoKrNetwork.kt b/core/network/src/main/java/com/android/mediproject/core/network/module/DataGoKrNetwork.kt index d8b6b3948..bb452abeb 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/module/DataGoKrNetwork.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/module/DataGoKrNetwork.kt @@ -6,7 +6,6 @@ import com.android.mediproject.core.common.network.Dispatcher import com.android.mediproject.core.common.network.MediDispatchers import com.android.mediproject.core.database.cache.manager.MedicineDataCacheManager import com.android.mediproject.core.model.medicine.medicineapproval.MedicineApprovalListResponse -import com.android.mediproject.core.model.medicine.medicinedetailinfo.MedicineDetailInfoResponse import com.android.mediproject.core.model.remote.adminaction.AdminActionListResponse import com.android.mediproject.core.model.remote.dur.DurResponse import com.android.mediproject.core.model.remote.elderlycaution.ElderlyCautionResponse @@ -37,8 +36,10 @@ import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import retrofit2.Response import retrofit2.Retrofit +import retrofit2.converter.scalars.ScalarsConverterFactory import retrofit2.http.GET import retrofit2.http.Query +import javax.inject.Named import javax.inject.Singleton const val DATA_GO_KR_BASEURL = "https://apis.data.go.kr/1471000/" @@ -49,46 +50,63 @@ object DataGoKrNetwork { @Provides @Singleton - fun providesDataGoKrNetworkApi(okHttpClient: OkHttpClient): DataGoKrNetworkApi = + @Named("dataGoKrNetworkApiWithJsonResponse") + fun providesDataGoKrNetworkApiWithJson(okHttpClient: OkHttpClient): DataGoKrNetworkApi = Retrofit.Builder().client(okHttpClient).addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) .baseUrl(DATA_GO_KR_BASEURL).build().create(DataGoKrNetworkApi::class.java) + @Provides + @Singleton + @Named("dataGoKrNetworkApiWithStringResponse") + fun providesDataGoKrNetworkApiWithString(okHttpClient: OkHttpClient): DataGoKrNetworkApi = + Retrofit.Builder().client(okHttpClient).addConverterFactory(ScalarsConverterFactory.create()).baseUrl(DATA_GO_KR_BASEURL).build() + .create(DataGoKrNetworkApi::class.java) + @Provides @Singleton fun providesRecallSuspensionDataSource( - @Dispatcher(MediDispatchers.IO) ioDispatcher: CoroutineDispatcher, dataGoKrNetworkApi: DataGoKrNetworkApi, - ): RecallSuspensionDataSource = RecallSuspensionDataSourceImpl(ioDispatcher, dataGoKrNetworkApi) + @Dispatcher(MediDispatchers.Default) defaultDispatcher: CoroutineDispatcher, + @Named("dataGoKrNetworkApiWithJsonResponse") dataGoKrNetworkApi: DataGoKrNetworkApi, + ): RecallSuspensionDataSource = RecallSuspensionDataSourceImpl(defaultDispatcher, dataGoKrNetworkApi) @Provides @Singleton fun providesAdminActionDataSource( - @Dispatcher(MediDispatchers.IO) ioDispatcher: CoroutineDispatcher, dataGoKrNetworkApi: DataGoKrNetworkApi, - ): AdminActionDataSource = AdminActionDataSourceImpl(ioDispatcher, dataGoKrNetworkApi) + @Dispatcher(MediDispatchers.Default) defaultDispatcher: CoroutineDispatcher, + @Named("dataGoKrNetworkApiWithJsonResponse") dataGoKrNetworkApi: DataGoKrNetworkApi, + ): AdminActionDataSource = AdminActionDataSourceImpl(defaultDispatcher, dataGoKrNetworkApi) @Provides @Singleton fun provideMedicineApprovalDataSource( - dataGoKrNetworkApi: DataGoKrNetworkApi, medicineDataCacheManager: MedicineDataCacheManager, + @Named("dataGoKrNetworkApiWithStringResponse") dataGoKrNetworkApiWithString: DataGoKrNetworkApi, + @Named("dataGoKrNetworkApiWithJsonResponse") dataGoKrNetworkApiWithJson: DataGoKrNetworkApi, + medicineDataCacheManager: MedicineDataCacheManager, googleSearchDataSource: GoogleSearchDataSource, - ): MedicineApprovalDataSource = MedicineApprovalDataSourceImpl( - dataGoKrNetworkApi, medicineDataCacheManager, googleSearchDataSource, + @Dispatcher(MediDispatchers.Default) defaultDispatcher: CoroutineDispatcher, + + ): MedicineApprovalDataSource = MedicineApprovalDataSourceImpl( + dataGoKrNetworkApiWithString, dataGoKrNetworkApiWithJson, medicineDataCacheManager, googleSearchDataSource, defaultDispatcher, ) @Provides @Singleton - fun providesGranuleIdentificationDataSource(dataGoKrNetworkApi: DataGoKrNetworkApi): GranuleIdentificationDataSource = - GranuleIdentificationDataSourceImpl(dataGoKrNetworkApi) + fun providesGranuleIdentificationDataSource( + @Named("dataGoKrNetworkApiWithJsonResponse") dataGoKrNetworkApi: DataGoKrNetworkApi, + ): GranuleIdentificationDataSource = GranuleIdentificationDataSourceImpl(dataGoKrNetworkApi) @Provides @Singleton - fun providesElderlyCautionDataSource(dataGoKrNetworkApi: DataGoKrNetworkApi): ElderlyCautionDataSource = - ElderlyCautionDataSourceImpl(dataGoKrNetworkApi) + fun providesElderlyCautionDataSource( + @Named("dataGoKrNetworkApiWithJsonResponse") dataGoKrNetworkApi: DataGoKrNetworkApi, + ): ElderlyCautionDataSource = ElderlyCautionDataSourceImpl(dataGoKrNetworkApi) @Provides @Singleton - fun providesDurDataSource(dataGoKrNetworkApi: DataGoKrNetworkApi): DurDataSource = DurDataSourceImpl(dataGoKrNetworkApi) + fun providesDurDataSource(@Named("dataGoKrNetworkApiWithJsonResponse") dataGoKrNetworkApi: DataGoKrNetworkApi): DurDataSource = + DurDataSourceImpl(dataGoKrNetworkApi) } interface DataGoKrNetworkApi { @@ -96,8 +114,8 @@ interface DataGoKrNetworkApi { @GET(value = "DrugPrdtPrmsnInfoService04/getDrugPrdtPrmsnInq04") suspend fun getApprovalList( @Query("serviceKey", encoded = true) serviceKey: String = BuildConfig.DATA_GO_KR_SERVICE_KEY, - @Query("item_name", encoded = true) itemName: String?, - @Query("entp_name", encoded = true) entpName: String?, + @Query("item_name", encoded = false) itemName: String?, + @Query("entp_name", encoded = false) entpName: String?, @Query("spclty_pblc", encoded = true) medicationType: String?, @Query("pageNo") pageNo: Int, @Query("type") type: String = JSON, @@ -108,12 +126,12 @@ interface DataGoKrNetworkApi { @GET(value = "DrugPrdtPrmsnInfoService04/getDrugPrdtPrmsnDtlInq03") suspend fun getMedicineDetailInfo( @Query("serviceKey", encoded = true) serviceKey: String = BuildConfig.DATA_GO_KR_SERVICE_KEY, - @Query("item_name", encoded = true) itemName: String = "", + @Query("item_name", encoded = false) itemName: String = "", @Query("item_seq", encoded = true) itemSeq: String = "", @Query("pageNo") pageNo: Int = 1, @Query("type") type: String = JSON, @Query("numOfRows") numOfRows: Int = 1, - ): Response + ): Response /** * 의약품 회수·판매중지 목록 조회 diff --git a/core/network/src/main/java/com/android/mediproject/core/network/module/NetworkModule.kt b/core/network/src/main/java/com/android/mediproject/core/network/module/NetworkModule.kt index 9d70e9d12..06a4ea60e 100644 --- a/core/network/src/main/java/com/android/mediproject/core/network/module/NetworkModule.kt +++ b/core/network/src/main/java/com/android/mediproject/core/network/module/NetworkModule.kt @@ -140,3 +140,5 @@ object NetworkModule { } } } + +internal fun String.safetyEncode(): String = replace("%", "%25").replace("&", "%26").replace("?", "%3F") diff --git a/feature/search/src/main/java/com/android/mediproject/feature/search/result/manual/ManualSearchResultViewModel.kt b/feature/search/src/main/java/com/android/mediproject/feature/search/result/manual/ManualSearchResultViewModel.kt index d250bfa51..9c216bb83 100644 --- a/feature/search/src/main/java/com/android/mediproject/feature/search/result/manual/ManualSearchResultViewModel.kt +++ b/feature/search/src/main/java/com/android/mediproject/feature/search/result/manual/ManualSearchResultViewModel.kt @@ -36,7 +36,7 @@ import javax.inject.Inject class ManualSearchResultViewModel @Inject constructor( private val getMedicineApprovalListUseCase: GetMedicineApprovalListUseCase, @Dispatcher(MediDispatchers.IO) private val ioDispatcher: CoroutineDispatcher, - @Dispatcher(MediDispatchers.Default) private val defaultDispatcher: CoroutineDispatcher + @Dispatcher(MediDispatchers.Default) private val defaultDispatcher: CoroutineDispatcher, ) : BaseViewModel(), ISendEvent { private val _searchParameter = @@ -93,17 +93,21 @@ class ManualSearchResultViewModel @Inject constructor( override fun send(e: ApprovedMedicineItemDto) { viewModelScope.launch(defaultDispatcher) { - _eventState.emit(EventState.OpenMedicineInfo(MedicineInfoArgs( - entpKorName = e.entpName, - entpEngName = e.entpEngName ?: "", - itemIngrName = e.itemIngrName, - itemKorName = e.itemName, - itemEngName = e.itemEngName ?: "", - itemSeq = e.itemSeq, - productType = e.prductType, - medicineType = e.medicineType, - imgUrl = e.imgUrl ?: "", - ))) + _eventState.emit( + EventState.OpenMedicineInfo( + MedicineInfoArgs( + entpKorName = e.entpName, + entpEngName = e.entpEngName ?: "", + itemIngrName = e.itemIngrName, + itemKorName = e.itemName, + itemEngName = e.itemEngName ?: "", + itemSeq = e.itemSeq, + productType = e.prductType, + medicineType = e.medicineType, + imgUrl = e.imgUrl ?: "", + ), + ), + ) } } -} \ No newline at end of file +}