角色相关接口
This commit is contained in:
		
							parent
							
								
									a792dddc7c
								
							
						
					
					
						commit
						48b23cbdd3
					
				| 
						 | 
				
			
			@ -129,8 +129,9 @@ android {
 | 
			
		|||
            buildConfigString("API_PIGEON", "https://test-pigeon.xxxx.ai")
 | 
			
		||||
            buildConfigString("API_LION", "https://test-lion.xxxx.ai")
 | 
			
		||||
            buildConfigString("RECHAEGE_SERVICES", "https://test.xxxxx.ai/policy/recharge")
 | 
			
		||||
 | 
			
		||||
            buildConfigString("RTC_APP_ID", "689ade491323ae01797818e0-XXX-TODO")
 | 
			
		||||
 | 
			
		||||
            buildConfigString("API_BASE", "http://54.223.196.180:9090")
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,8 +148,9 @@ android {
 | 
			
		|||
            buildConfigString("API_PIGEON", "https://test-pigeon.xxxx.ai")
 | 
			
		||||
            buildConfigString("API_LION", "https://test-lion.xxxx.ai")
 | 
			
		||||
            buildConfigString("RECHAEGE_SERVICES", "https://test.xxxxx.ai/policy/recharge")
 | 
			
		||||
 | 
			
		||||
            buildConfigString("RTC_APP_ID", "689ade491323ae01797818e0-XXX-TODO")
 | 
			
		||||
 | 
			
		||||
            buildConfigString("API_BASE", "http://54.223.196.180:9090")
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,6 +31,7 @@
 | 
			
		|||
 | 
			
		||||
    <uses-feature android:name="android.hardware.camera" android:required="false" />
 | 
			
		||||
 | 
			
		||||
    <!--TODO - remove usesCleartextTraffic below-->
 | 
			
		||||
    <application
 | 
			
		||||
        android:name=".configs.NovelApplication"
 | 
			
		||||
        android:allowBackup="true"
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +41,11 @@
 | 
			
		|||
        android:label="@string/app_name"
 | 
			
		||||
        android:roundIcon="@mipmap/ic_launcher_round"
 | 
			
		||||
        android:supportsRtl="true"
 | 
			
		||||
        android:theme="@style/AppTheme">
 | 
			
		||||
        android:theme="@style/AppTheme"
 | 
			
		||||
 | 
			
		||||
        android:usesCleartextTraffic="true"
 | 
			
		||||
        android:networkSecurityConfig="@xml/network_security_config"
 | 
			
		||||
        >
 | 
			
		||||
        <activity
 | 
			
		||||
            android:name=".ui.splash.SplashActivity"
 | 
			
		||||
            android:theme="@style/AppTheme.Launcher"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
package com.remax.visualnovel.api.service
 | 
			
		||||
 | 
			
		||||
import com.remax.visualnovel.BuildConfig
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ResponseNew
 | 
			
		||||
import com.remax.visualnovel.entity.request.ParamActorList
 | 
			
		||||
import com.remax.visualnovel.entity.response.ActorBean
 | 
			
		||||
import com.remax.visualnovel.entity.response.ActorTag
 | 
			
		||||
import retrofit2.http.Body
 | 
			
		||||
import retrofit2.http.GET
 | 
			
		||||
import retrofit2.http.POST
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
interface ActorsService {
 | 
			
		||||
    @GET(BuildConfig.API_BASE + "/tag/getTags")
 | 
			
		||||
    suspend fun requestActorTags(): ResponseNew<List<ActorTag>>
 | 
			
		||||
 | 
			
		||||
    @POST(BuildConfig.API_BASE + "/character/select/list")
 | 
			
		||||
    suspend fun requestActorList(@Body param: ParamActorList): ResponseNew<List<ActorBean>>
 | 
			
		||||
 | 
			
		||||
    /*@GET(BuildConfig.API_BASE + "/character/select/roleInfo/{roleId}")
 | 
			
		||||
    suspend fun requestActorInfo(): ResponseNew<ActorTag>*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package com.remax.visualnovel.app.di
 | 
			
		|||
 | 
			
		||||
import com.remax.visualnovel.api.factory.ServiceFactory
 | 
			
		||||
import com.remax.visualnovel.api.service.AIService
 | 
			
		||||
import com.remax.visualnovel.api.service.ActorsService
 | 
			
		||||
import com.remax.visualnovel.api.service.BookService
 | 
			
		||||
import com.remax.visualnovel.api.service.ChatService
 | 
			
		||||
import com.remax.visualnovel.api.service.DictService
 | 
			
		||||
| 
						 | 
				
			
			@ -64,4 +65,10 @@ object ApiServiceModule {
 | 
			
		|||
    private inline fun <reified T> create(): T {
 | 
			
		||||
        return ServiceFactory.createService()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Singleton
 | 
			
		||||
    @Provides
 | 
			
		||||
    fun actorsService() = create<ActorsService>()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
package com.remax.visualnovel.entity.request
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
data class ParamActorList(
 | 
			
		||||
    var index: Int = 0,
 | 
			
		||||
    var limit: Int = 2,
 | 
			
		||||
    var tagIds: List<Long> = listOf<Long>(),
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
package com.remax.visualnovel.entity.response
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
data class ActorBean(
 | 
			
		||||
    val id: Long,
 | 
			
		||||
    val roleName: String,
 | 
			
		||||
    val description: String,
 | 
			
		||||
    val coverImageId: String,
 | 
			
		||||
    val sourceId: Int,  //来源ID;所属书籍/漫剧
 | 
			
		||||
    val sourceType: Int,  //来源分类
 | 
			
		||||
    val commonCount: Int,  //评论数
 | 
			
		||||
    val createTime: String,
 | 
			
		||||
    val status: String
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
package com.remax.visualnovel.entity.response
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
data class ActorTag(
 | 
			
		||||
    val tagName: String,
 | 
			
		||||
    val tagId: Long
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
package com.remax.visualnovel.entity.response.basenew
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ResponseData<T> {
 | 
			
		||||
    val total = 0
 | 
			
		||||
    val index = 0
 | 
			
		||||
    val limit = 0
 | 
			
		||||
    val rows: T? = null
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,88 @@
 | 
			
		|||
package com.remax.visualnovel.entity.response.basenew
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import com.google.gson.annotations.SerializedName
 | 
			
		||||
import com.remax.visualnovel.app.base.app.CommonApplicationProxy
 | 
			
		||||
import com.remax.visualnovel.extension.toast
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
open class ResponseNew<T>(
 | 
			
		||||
    @SerializedName(value = "data")
 | 
			
		||||
    val data: ResponseData<T>? = null,
 | 
			
		||||
    open val code: Int = -1,
 | 
			
		||||
    open val message: String = "",
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    companion object {
 | 
			
		||||
        const val SuccessCode = 200
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * zip打包的错误error封装
 | 
			
		||||
         * new
 | 
			
		||||
         */
 | 
			
		||||
        inline fun <reified T> createZipFailResponse(vararg data: ResponseNew<*>): ApiFailedResponse<T> {
 | 
			
		||||
            val failedResponse = ApiFailedResponse<T>()
 | 
			
		||||
            for (t in data) {
 | 
			
		||||
                if (!t.isApiSuccess) {
 | 
			
		||||
                    failedResponse.code = t.code
 | 
			
		||||
                    failedResponse.message = t.message
 | 
			
		||||
                    break
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return failedResponse
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    val isOk: Boolean
 | 
			
		||||
        get() = code == SuccessCode
 | 
			
		||||
 | 
			
		||||
    val isApiSuccess: Boolean
 | 
			
		||||
        get() =
 | 
			
		||||
            this is ApiSuccessResponse || this is ApiEmptyResponse
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 将返回结果分为成功和失败2个高阶函数
 | 
			
		||||
     *
 | 
			
		||||
     * 使用inline修饰,使2个参数可以调用外部函数return
 | 
			
		||||
     */
 | 
			
		||||
    inline fun transformResult(apiSuccessCallback: ((T?) -> Unit) = {}, apiFailedCallback: ((ResponseNew<T>) -> Unit) = {}): ResponseNew<T> {
 | 
			
		||||
        if (isApiSuccess) {
 | 
			
		||||
            apiSuccessCallback.invoke(data?.rows)
 | 
			
		||||
        } else {
 | 
			
		||||
            apiFailedCallback.invoke(this)
 | 
			
		||||
        }
 | 
			
		||||
        return this
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified T> ResponseNew<T>.parseData(listenerBuilder: (ResultBuilder<T>.() -> Unit), showToast: Boolean = false) {
 | 
			
		||||
    val listener = ResultBuilder<T>().also(listenerBuilder)
 | 
			
		||||
    when (this) {
 | 
			
		||||
        is ApiSuccessResponse -> listener.onSuccess(response?.rows)
 | 
			
		||||
        is ApiEmptyResponse -> listener.onSuccess(null)
 | 
			
		||||
        is ApiFailedResponse -> {
 | 
			
		||||
            listener.onFailed(this.code, this.message)
 | 
			
		||||
            listener.onFailedWithData(data?.rows)
 | 
			
		||||
            if (showToast) {
 | 
			
		||||
                CommonApplicationProxy.application.toast(message)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    listener.onComplete()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class ResultBuilder<T> {
 | 
			
		||||
    var onSuccess: (data: T?) -> Unit = {}
 | 
			
		||||
    var onFailed: (errorCode: Int, errorMsg: String) -> Unit = { _, _ ->
 | 
			
		||||
    }
 | 
			
		||||
    var onFailedWithData: (errorData: T?) -> Unit = {}
 | 
			
		||||
    var onComplete: () -> Unit = {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
data class ApiSuccessResponse<T>(val response: ResponseData<T>? = null) : ResponseNew<T>(data = response)
 | 
			
		||||
 | 
			
		||||
class ApiEmptyResponse<T> : ResponseNew<T>()
 | 
			
		||||
 | 
			
		||||
data class ApiFailedResponse<T>(override var code: Int = -1, override var message: String = "") :
 | 
			
		||||
    ResponseNew<T>(code = code, message = message)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,144 @@
 | 
			
		|||
package com.remax.visualnovel.extension
 | 
			
		||||
 | 
			
		||||
import androidx.lifecycle.Lifecycle
 | 
			
		||||
import androidx.lifecycle.LifecycleOwner
 | 
			
		||||
import androidx.lifecycle.LiveData
 | 
			
		||||
import androidx.lifecycle.Observer
 | 
			
		||||
import androidx.lifecycle.lifecycleScope
 | 
			
		||||
import androidx.lifecycle.repeatOnLifecycle
 | 
			
		||||
import com.remax.visualnovel.app.AbsView
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ResponseNew
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ResultBuilder
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.parseData
 | 
			
		||||
import kotlinx.coroutines.Dispatchers
 | 
			
		||||
import kotlinx.coroutines.Job
 | 
			
		||||
import kotlinx.coroutines.flow.Flow
 | 
			
		||||
import kotlinx.coroutines.flow.callbackFlow
 | 
			
		||||
import kotlinx.coroutines.flow.collect
 | 
			
		||||
import kotlinx.coroutines.flow.flow
 | 
			
		||||
import kotlinx.coroutines.flow.flowOn
 | 
			
		||||
import kotlinx.coroutines.flow.onCompletion
 | 
			
		||||
import kotlinx.coroutines.flow.onStart
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
 | 
			
		||||
inline fun <T> launchFlow2(
 | 
			
		||||
    crossinline requestBlock: suspend () -> ResponseNew<T>,
 | 
			
		||||
    noinline startCallback: (() -> Unit)? = null,
 | 
			
		||||
    noinline completeCallback: (() -> Unit)? = null,
 | 
			
		||||
): Flow<ResponseNew<T>> {
 | 
			
		||||
    return flow {
 | 
			
		||||
        emit(requestBlock())
 | 
			
		||||
    }.onStart {
 | 
			
		||||
        startCallback?.invoke()
 | 
			
		||||
    }.onCompletion {
 | 
			
		||||
        completeCallback?.invoke()
 | 
			
		||||
    }.flowOn(Dispatchers.Main)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 简单的请求,不返回任何实体类, 无loading和toast,数据可通过livedata/flow监听
 | 
			
		||||
 */
 | 
			
		||||
inline fun AbsView.launchWithRequest2(crossinline requestBlock: suspend () -> Unit, showLoading: Boolean = false) {
 | 
			
		||||
    lifecycleScope.launch {
 | 
			
		||||
        flow {
 | 
			
		||||
            emit(requestBlock())
 | 
			
		||||
        }.onStart {
 | 
			
		||||
            if (showLoading) showLoading()
 | 
			
		||||
        }.onCompletion {
 | 
			
		||||
            if (showLoading) hideLoading()
 | 
			
		||||
        }.collect()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 调用上面的,默认loading
 | 
			
		||||
 */
 | 
			
		||||
inline fun AbsView.launchWithRequestLoading2(crossinline requestBlock: suspend () -> Unit) {
 | 
			
		||||
    launchWithRequest(requestBlock, true)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 链式调用,返回结果的处理都在一起,viewmodel中不需要创建一个livedata对象
 | 
			
		||||
 * 适用于不需要监听数据变化/一次性使用的场景,比如提交表单/登录
 | 
			
		||||
 * 屏幕旋转,Activity销毁重建,数据会消失
 | 
			
		||||
 *
 | 
			
		||||
 * 默认无toast,无loading
 | 
			
		||||
 */
 | 
			
		||||
inline fun <reified T> AbsView.launchAndCollect2(
 | 
			
		||||
    crossinline requestBlock: suspend () -> ResponseNew<T>,
 | 
			
		||||
    showLoading: Boolean = false,
 | 
			
		||||
    showToast: Boolean = true,
 | 
			
		||||
    crossinline listenerBuilder: (ResultBuilder<T>.() -> Unit) = {}
 | 
			
		||||
) {
 | 
			
		||||
    lifecycleScope.launch {
 | 
			
		||||
        launchFlow2(requestBlock, { if (showLoading) showLoading() }) { if (showLoading) hideLoading() }.collect { response ->
 | 
			
		||||
            response.parseData(listenerBuilder, showToast)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline fun <reified T> AbsView.launchAndLoadingCollect2(
 | 
			
		||||
    crossinline requestBlock: suspend () -> ResponseNew<T>, showToast: Boolean = true, crossinline listenerBuilder: (ResultBuilder<T>.() -> Unit) = {}
 | 
			
		||||
) {
 | 
			
		||||
    launchAndCollect2(requestBlock, showLoading = true, showToast = showToast, listenerBuilder = listenerBuilder)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 简单flow流订阅 生命周期安全
 | 
			
		||||
 */
 | 
			
		||||
inline fun <T> Flow<T?>.flowWithLaunch2(
 | 
			
		||||
    lifecycleOwner: LifecycleOwner, minActiveState: Lifecycle.State = Lifecycle.State.STARTED, crossinline resCallback: ((t: T?) -> Unit)
 | 
			
		||||
) {
 | 
			
		||||
    lifecycleOwner.lifecycleScope.launch {
 | 
			
		||||
        flowWithLifecycle(lifecycleOwner.lifecycle, minActiveState).collect {
 | 
			
		||||
            resCallback.invoke(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
fun <T> Flow<T>.flowWithLifecycle2(lifecycle: Lifecycle, minActiveState: Lifecycle.State = Lifecycle.State.STARTED): Flow<T> = callbackFlow {
 | 
			
		||||
    lifecycle.repeatOnLifecycle(minActiveState) {
 | 
			
		||||
        this@flowWithLifecycle2.collect {
 | 
			
		||||
            send(it)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    close()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * response liveData监听
 | 
			
		||||
 */
 | 
			
		||||
inline fun <reified T> LiveData<ResponseNew<T>>.observeIn2(
 | 
			
		||||
    lifecycleOwner: LifecycleOwner, showToast: Boolean = true, crossinline listenerBuilder: ResultBuilder<T>.() -> Unit
 | 
			
		||||
) {
 | 
			
		||||
    this.observe(lifecycleOwner, Observer {
 | 
			
		||||
        it.parseData(listenerBuilder, showToast)
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 订阅UI上展示Flow数据流
 | 
			
		||||
 *
 | 
			
		||||
 * 状态(State)用 StateFlow,粘性的 ;事件(Event)用 SharedFlow 在其 replayCache 中保留特定数量的最新值
 | 
			
		||||
 * MutableSharedFlow :一次性事件,不需要重放的状态变更(例如 Toast)
 | 
			
		||||
 * MutableStateFlow : 页面需要的状态,比如UI的刷新,多次执行没有任何问题
 | 
			
		||||
 * collectLastValue = true时,stateFlow也不会发送未改变的value,就和sharedFlow一样的用法
 | 
			
		||||
 */
 | 
			
		||||
inline fun <reified T> Flow<ResponseNew<T>>.collectIn2(
 | 
			
		||||
    lifecycleOwner: LifecycleOwner,
 | 
			
		||||
    minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
 | 
			
		||||
    showToast: Boolean = true,
 | 
			
		||||
    crossinline listenerBuilder: ResultBuilder<T>.() -> Unit
 | 
			
		||||
): Job {
 | 
			
		||||
    return lifecycleOwner.lifecycleScope.launch {
 | 
			
		||||
        flowWithLifecycle2(lifecycleOwner.lifecycle, minActiveState).collect {
 | 
			
		||||
            it.parseData(listenerBuilder, showToast)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,14 +1,18 @@
 | 
			
		|||
package com.remax.visualnovel.repository.api
 | 
			
		||||
 | 
			
		||||
import com.remax.visualnovel.entity.response.Book
 | 
			
		||||
import com.remax.visualnovel.repository.api.base.BaseRepository
 | 
			
		||||
import com.remax.visualnovel.api.service.BookService
 | 
			
		||||
import com.remax.visualnovel.entity.response.base.Response
 | 
			
		||||
import com.remax.visualnovel.api.service.ActorsService
 | 
			
		||||
import com.remax.visualnovel.entity.request.ParamActorList
 | 
			
		||||
import com.remax.visualnovel.repository.api.base.BaseRepositoryNew
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ActorsRepository @Inject constructor(private val bookService: BookService) : BaseRepository() {
 | 
			
		||||
        suspend fun getBooks(): Response<Book> {
 | 
			
		||||
            return bookService.getBooks()
 | 
			
		||||
        }
 | 
			
		||||
class ActorsRepository @Inject constructor(private val mActorsService: ActorsService) : BaseRepositoryNew() {
 | 
			
		||||
    suspend fun getActorTags() = executeHttp {
 | 
			
		||||
        mActorsService.requestActorTags()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suspend fun getActorList(param: ParamActorList) = executeHttp {
 | 
			
		||||
        mActorsService.requestActorList(param)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,93 @@
 | 
			
		|||
package com.remax.visualnovel.repository.api.base
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import com.remax.visualnovel.R
 | 
			
		||||
import com.remax.visualnovel.app.base.app.CommonApplicationProxy
 | 
			
		||||
import com.remax.visualnovel.constant.StatusCode
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ApiEmptyResponse
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ApiFailedResponse
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ApiSuccessResponse
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ResponseNew
 | 
			
		||||
import com.remax.visualnovel.extension.toast
 | 
			
		||||
import com.remax.visualnovel.manager.login.LoginManager
 | 
			
		||||
import timber.log.Timber
 | 
			
		||||
 | 
			
		||||
open class BaseRepositoryNew {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 如果不需要检查登录,那么在未登录的情况下 直接返回Success
 | 
			
		||||
     * @param checkLogin  检查登录,默认都需要检查
 | 
			
		||||
     */
 | 
			
		||||
    suspend fun <T> executeHttp(checkLogin: Boolean = true, block: suspend () -> ResponseNew<T>): ResponseNew<T> {
 | 
			
		||||
        return if (!checkLogin) {
 | 
			
		||||
            if (LoginManager.isLogin) {
 | 
			
		||||
                execute(block)
 | 
			
		||||
            } else {
 | 
			
		||||
                ApiSuccessResponse()
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            execute(block)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private suspend fun <T> execute(block: suspend () -> ResponseNew<T>): ResponseNew<T> {
 | 
			
		||||
        return try {
 | 
			
		||||
            val data = block.invoke()
 | 
			
		||||
            handleHttpOk(data)
 | 
			
		||||
        } catch (e: Exception) {
 | 
			
		||||
            handleHttpError(e)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 非后台返回错误,捕获到的异常
 | 
			
		||||
     */
 | 
			
		||||
    private fun <T> handleHttpError(e: Throwable): ApiFailedResponse<T> {
 | 
			
		||||
        Timber.e("responseAsync error -> ${e.localizedMessage}")
 | 
			
		||||
        val errorMsg = CommonApplicationProxy.application.getString(R.string.your_network_error)
 | 
			
		||||
        return ApiFailedResponse(message = errorMsg)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * http返回200,还要判断后端业务层isSuccess
 | 
			
		||||
     */
 | 
			
		||||
    private fun <T> handleHttpOk(response: ResponseNew<T>): ResponseNew<T> {
 | 
			
		||||
        return when {
 | 
			
		||||
            //后端业务正常
 | 
			
		||||
            response.isOk -> {
 | 
			
		||||
                getHttpSuccessResponse(response)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //登录超时
 | 
			
		||||
            response.code == /*StatusCode.TOKEN_EXPIRED.code*/403 -> {
 | 
			
		||||
                CommonApplicationProxy.application.toast(response.message)
 | 
			
		||||
                LoginManager.logout()
 | 
			
		||||
                ApiFailedResponse(response.code, response.message)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            //余额不足
 | 
			
		||||
            response.code == /*StatusCode.INSUFFICIENT_BALANCE.code*/ 610 -> {
 | 
			
		||||
                /*WalletManager.refreshWallet()
 | 
			
		||||
                WalletManager.showChargeDialog()*/
 | 
			
		||||
                ApiFailedResponse(response.code, response.message)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            else -> {
 | 
			
		||||
                ApiFailedResponse(response.code, response.message)
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 成功和数据为空的处理
 | 
			
		||||
     */
 | 
			
		||||
    private fun <T> getHttpSuccessResponse(response: ResponseNew<T>): ResponseNew<T> {
 | 
			
		||||
        val data = response.data
 | 
			
		||||
        return if (data == null) {
 | 
			
		||||
            ApiEmptyResponse()
 | 
			
		||||
        } else {
 | 
			
		||||
            ApiSuccessResponse(data)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -9,11 +9,14 @@ import androidx.recyclerview.widget.DefaultItemAnimator
 | 
			
		|||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
 | 
			
		||||
import com.alibaba.android.arouter.facade.annotation.Route
 | 
			
		||||
import com.alibaba.android.arouter.launcher.ARouter
 | 
			
		||||
import com.chad.library.adapter.base.loadmore.LoadMoreStatus
 | 
			
		||||
import com.dylanc.loadingstateview.BgColorType
 | 
			
		||||
import com.remax.visualnovel.R
 | 
			
		||||
import com.remax.visualnovel.app.base.BaseBindingFragment
 | 
			
		||||
import com.remax.visualnovel.configs.NovelApplication
 | 
			
		||||
import com.remax.visualnovel.databinding.FragmentMainActorBinding
 | 
			
		||||
import com.remax.visualnovel.entity.request.ParamActorList
 | 
			
		||||
import com.remax.visualnovel.extension.launchAndCollect2
 | 
			
		||||
import com.remax.visualnovel.utils.Routers
 | 
			
		||||
import com.remax.visualnovel.utils.StatusBarUtil3
 | 
			
		||||
import dagger.hilt.android.AndroidEntryPoint
 | 
			
		||||
| 
						 | 
				
			
			@ -26,7 +29,10 @@ import kotlin.math.max
 | 
			
		|||
class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
 | 
			
		||||
 | 
			
		||||
    private lateinit var mActorAdapter: ActorsAdapter
 | 
			
		||||
    private val actorsViewModel by viewModels<ActorListViewModel>()
 | 
			
		||||
    private val mActorsModel by viewModels<ActorsViewModel>()
 | 
			
		||||
    private var mLoadedPageIndex = 0
 | 
			
		||||
    private val mRequestParam by lazy { ParamActorList() }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    override fun onCreated(bundle: Bundle?) {
 | 
			
		||||
        setUI()
 | 
			
		||||
| 
						 | 
				
			
			@ -36,6 +42,10 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
 | 
			
		|||
        return BgColorType.TRANSPARENT
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    override fun lazyInit() {
 | 
			
		||||
        getActorList(true, showLoading = false)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private fun setUI() {
 | 
			
		||||
        with(binding.root) {
 | 
			
		||||
            setPadding(
 | 
			
		||||
| 
						 | 
				
			
			@ -59,11 +69,22 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
 | 
			
		|||
            mActorsRv.addItemDecoration(GridSpacingItemDecoration(16))
 | 
			
		||||
            mActorsRv.setHasFixedSize(true)
 | 
			
		||||
            mActorsRv.itemAnimator = DefaultItemAnimator()
 | 
			
		||||
 | 
			
		||||
            mActorAdapter = ActorsAdapter()
 | 
			
		||||
            mActorsRv.adapter = mActorAdapter
 | 
			
		||||
 | 
			
		||||
            val characterList = createSampleData()
 | 
			
		||||
            mActorAdapter.setList(characterList)
 | 
			
		||||
            with(mActorAdapter) {
 | 
			
		||||
                setList(characterList)
 | 
			
		||||
                loadMoreModule.setOnLoadMoreListener {
 | 
			
		||||
                    getActorList(false, showLoading = false)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            with(refreshLayout) {
 | 
			
		||||
                onRefresh {
 | 
			
		||||
                    getActorList(true, showLoading = true)
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -94,6 +115,55 @@ class ActorListFragment : BaseBindingFragment<FragmentMainActorBinding>() {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun getActorList(isRefresh: Boolean, showLoading: Boolean) {
 | 
			
		||||
        if (isRefresh) {
 | 
			
		||||
            mLoadedPageIndex = 0
 | 
			
		||||
        }
 | 
			
		||||
        mRequestParam.index = mLoadedPageIndex + 1
 | 
			
		||||
 | 
			
		||||
        launchAndCollect2({
 | 
			
		||||
            mActorsModel.getActorList(mRequestParam)
 | 
			
		||||
        }, showLoading = showLoading) {
 | 
			
		||||
            onSuccess = {
 | 
			
		||||
                val data = it ?: emptyList()
 | 
			
		||||
                with(mActorAdapter) {
 | 
			
		||||
                    /*if (isRefresh) {
 | 
			
		||||
                        setList(data)
 | 
			
		||||
                        setMyEmptyView(R.string.no_character_yet, topMargin = 60)
 | 
			
		||||
                    } else {
 | 
			
		||||
                        addData(data)
 | 
			
		||||
                        loadMoreModule.loadMoreComplete()
 | 
			
		||||
                    }
 | 
			
		||||
                    if (data.size < PageQuery.DEFAULT_PAGE_SIZE) {
 | 
			
		||||
                        loadMoreModule.loadMoreEnd()
 | 
			
		||||
                    }*/
 | 
			
		||||
                }
 | 
			
		||||
                mLoadedPageIndex++
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            onComplete = {
 | 
			
		||||
                binding.refreshLayout.finishRefresh()
 | 
			
		||||
                mActorAdapter.loadMoreModule.loadMoreComplete()
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            onFailed = { errorCode, errorMsg ->
 | 
			
		||||
                var temp = 100
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            onFailedWithData = {
 | 
			
		||||
                var temp = 100
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private fun createSampleData(): List<ActorItem> {
 | 
			
		||||
        return listOf(
 | 
			
		||||
            ActorItem(
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,25 +0,0 @@
 | 
			
		|||
package com.remax.visualnovel.ui.main.actor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import com.remax.visualnovel.entity.response.Book
 | 
			
		||||
import com.remax.visualnovel.app.viewmodel.base.BaseViewModel
 | 
			
		||||
import com.remax.visualnovel.entity.response.base.Response
 | 
			
		||||
import com.remax.visualnovel.repository.api.ActorsRepository
 | 
			
		||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
			
		||||
import kotlinx.coroutines.flow.MutableSharedFlow
 | 
			
		||||
import kotlinx.coroutines.flow.asSharedFlow
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@HiltViewModel
 | 
			
		||||
class ActorListViewModel @Inject constructor(private val chatRepository: ActorsRepository) : BaseViewModel() {
 | 
			
		||||
 | 
			
		||||
    private val _msgStatFlow = MutableSharedFlow<Response<Book>>()
 | 
			
		||||
    val msgStatFlow = _msgStatFlow.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    suspend fun getMessageStat() {
 | 
			
		||||
        _msgStatFlow.emit(chatRepository.getBooks())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
package com.remax.visualnovel.ui.main.actor
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import com.remax.visualnovel.app.viewmodel.base.BaseViewModel
 | 
			
		||||
import com.remax.visualnovel.entity.request.ParamActorList
 | 
			
		||||
import com.remax.visualnovel.entity.response.basenew.ResponseNew
 | 
			
		||||
import com.remax.visualnovel.entity.response.ActorBean
 | 
			
		||||
import com.remax.visualnovel.repository.api.ActorsRepository
 | 
			
		||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
			
		||||
import kotlinx.coroutines.flow.MutableSharedFlow
 | 
			
		||||
import kotlinx.coroutines.flow.asSharedFlow
 | 
			
		||||
import javax.inject.Inject
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@HiltViewModel
 | 
			
		||||
class ActorsViewModel @Inject constructor(private val mActorsRepository: ActorsRepository) : BaseViewModel() {
 | 
			
		||||
 | 
			
		||||
    private val _actorsStatFlow = MutableSharedFlow<ResponseNew<List<ActorBean>>>()
 | 
			
		||||
    val actorsStatFlow = _actorsStatFlow.asSharedFlow()
 | 
			
		||||
 | 
			
		||||
    suspend fun getActorList(param: ParamActorList) = mActorsRepository.getActorList(param)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<LinearLayout
 | 
			
		||||
<com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout
 | 
			
		||||
    xmlns:android="http://schemas.android.com/apk/res/android"
 | 
			
		||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
			
		||||
    android:layout_width="match_parent"
 | 
			
		||||
| 
						 | 
				
			
			@ -27,12 +27,17 @@
 | 
			
		|||
        app:tag_expand_drawable="@mipmap/tag_flow_expand"
 | 
			
		||||
        app:tag_shrink_drawable="@mipmap/tag_flow_shrink" />
 | 
			
		||||
 | 
			
		||||
    <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
        android:id="@+id/m_actors_rv"
 | 
			
		||||
    <com.drake.brv.PageRefreshLayout
 | 
			
		||||
        android:id="@+id/refreshLayout"
 | 
			
		||||
        android:layout_width="match_parent"
 | 
			
		||||
        android:layout_height="0dp"
 | 
			
		||||
        android:layout_weight="1"
 | 
			
		||||
        android:layout_marginTop="@dimen/dp_4"
 | 
			
		||||
        />
 | 
			
		||||
        android:layout_height="match_parent"
 | 
			
		||||
        android:layout_marginTop="@dimen/dp_4">
 | 
			
		||||
        <androidx.recyclerview.widget.RecyclerView
 | 
			
		||||
            android:id="@+id/m_actors_rv"
 | 
			
		||||
            android:layout_width="match_parent"
 | 
			
		||||
            android:layout_height="wrap_content"
 | 
			
		||||
            />
 | 
			
		||||
    </com.drake.brv.PageRefreshLayout>
 | 
			
		||||
 | 
			
		||||
</LinearLayout>
 | 
			
		||||
 | 
			
		||||
</com.remax.visualnovel.widget.uitoken.view.UITokenLinearLayout>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
<?xml version="1.0" encoding="utf-8"?>
 | 
			
		||||
<network-security-config>
 | 
			
		||||
    <domain-config cleartextTrafficPermitted="true">
 | 
			
		||||
        <domain includeSubdomains="true">54.223.196.180</domain>
 | 
			
		||||
    </domain-config>
 | 
			
		||||
</network-security-config>
 | 
			
		||||
		Loading…
	
		Reference in New Issue