# 网络请求 MVVM 架构封装方案（优化版）

## 一、设计目标

将现有的 `ApiResponseParser` 和 `ApiServiceFactory` 封装成符合 MVVM 架构的完整 Data 层组件，统一处理公共逻辑，业务层只需简单调用。

## 二、架构设计

### 2.1 现有组件

- ✅ `ApiResponseParser` - 响应解析器（已存在）
- ✅ `ApiServiceFactory` - API 服务工厂（已存在）

### 2.2 新增组件

- BaseRemoteDataSource - 基础远程数据源抽象类（增强版）

  - 封装通用网络请求逻辑
  - 统一错误处理、异常捕获、日志记录
  - 使用 reified 泛型简化调用，无需传入 dataClass
  - 自动切换到 IO 线程

- BaseRepository - 基础仓库类（可选）

  - 封装通用仓库逻辑
  - 统一数据转换和缓存处理

## 三、实现方案

### 3.1 BaseRemoteDataSource 抽象类（增强版）

    package com.narutohuo.xindazhou.common.network

    import android.util.Log
    import retrofit2.Response
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.withContext
    import java.net.UnknownHostException
    import java.net.SocketTimeoutException

    /**
     * 基础远程数据源
     *
     * 封装通用网络请求逻辑，统一处理：
     * - 错误处理和异常捕获
     * - 日志记录
     * - 线程切换（自动切换到 IO 线程）
     * - 网络异常友好提示
     *
     * 使用方式：
     * ```kotlin
     * class AuthRemoteDataSource : BaseRemoteDataSource() {
     *     private val authApi: AuthApi = ApiServiceFactory.create()
     *
     *     suspend fun login(request: LoginRequest): Result<LoginResponse> {
     *         return executeRequest(
     *             request = { authApi.login(request) },
     *             errorMessage = "登录失败"
     *         )
     *     }
     * }
     * ```
     */
    abstract class BaseRemoteDataSource {

        /**
         * 获取日志标签（子类可重写）
         */
        protected open val tag: String
            get() = this::class.simpleName ?: "BaseRemoteDataSource"

        /**
         * 处理网络请求（通用方法）
         *
         * 自动处理：
         * - HTTP 错误码
         * - 业务错误码
         * - 网络异常
         * - 超时异常
         * - 日志记录
         *
         * @param request 网络请求的 suspend 函数
         * @param errorMessage 错误消息前缀
         * @return Result<T>
         */
        protected suspend inline fun <reified T> executeRequest(
            crossinline request: suspend () -> Response<CommonResult<T>>,
            errorMessage: String = "请求失败"
        ): Result<T> = withContext(Dispatchers.IO) {
            try {
                Log.d(tag, "开始请求: ${T::class.simpleName}")
                val response = request()

                // 使用 reified 泛型，自动获取 Class
                ApiResponseParser.handleResponse(
                    response = response,
                    dataClass = T::class.java,
                    tag = tag,
                    errorMessage = errorMessage
                )
            } catch (e: SocketTimeoutException) {
                val errorMsg = "请求超时，请检查网络连接"
                Log.e(tag, errorMsg, e)
                Result.failure(Exception(errorMsg))
            } catch (e: UnknownHostException) {
                val errorMsg = "网络连接失败，请检查网络设置"
                Log.e(tag, errorMsg, e)
                Result.failure(Exception(errorMsg))
            } catch (e: Exception) {
                val errorMsg = e.message ?: errorMessage
                Log.e(tag, "请求异常: $errorMsg", e)
                Result.failure(Exception(errorMsg))
            }
        }

        /**
         * 处理可空数据的网络请求
         *
         * 用于处理服务器可能返回 null 的情况
         *
         * @param request 网络请求的 suspend 函数
         * @param errorMessage 错误消息前缀
         * @return Result<T?>
         */
        protected suspend inline fun <reified T> executeNullableRequest(
            crossinline request: suspend () -> Response<CommonResult<T>>,
            errorMessage: String = "请求失败"
        ): Result<T?> = withContext(Dispatchers.IO) {
            try {
                Log.d(tag, "开始请求（可空）: ${T::class.simpleName}")
                val response = request()

                ApiResponseParser.handleNullableResponse(
                    response = response,
                    tag = tag,
                    errorMessage = errorMessage
                )
            } catch (e: SocketTimeoutException) {
                val errorMsg = "请求超时，请检查网络连接"
                Log.e(tag, errorMsg, e)
                Result.failure(Exception(errorMsg))
            } catch (e: UnknownHostException) {
                val errorMsg = "网络连接失败，请检查网络设置"
                Log.e(tag, errorMsg, e)
                Result.failure(Exception(errorMsg))
            } catch (e: Exception) {
                val errorMsg = e.message ?: errorMessage
                Log.e(tag, "请求异常: $errorMsg", e)
                Result.failure(Exception(errorMsg))
            }
        }
    }

### 3.2 BaseRepository 基础仓库类（可选）

    package com.narutohuo.xindazhou.common.network

    import android.util.Log

    /**
     * 基础仓库类
     *
     * 封装通用仓库逻辑：
     * - 统一错误处理
     * - 数据转换（Data Model → Domain Model）
     * - 缓存策略（可选）
     *
     * 使用方式：
     * ```kotlin
     * class AuthRepository(
     *     private val remoteDataSource: AuthRemoteDataSource
     * ) : BaseRepository() {
     *
     *     suspend fun login(mobile: String, password: String): Result<LoginResponse> {
     *         val request = LoginRequest(mobile, password)
     *         return remoteDataSource.login(request)
     *     }
     * }
     * ```
     */
    open class BaseRepository {

        /**
         * 获取日志标签（子类可重写）
         */
        protected open val tag: String
            get() = this::class.simpleName ?: "BaseRepository"

        /**
         * 处理数据转换（Data Model → Domain Model）
         *
         * 子类可重写此方法实现自定义转换逻辑
         */
        protected open fun <T> transform(data: T): T {
            return data
        }

        /**
         * 统一错误处理
         *
         * 子类可重写此方法实现自定义错误处理
         */
        protected open fun handleError(throwable: Throwable): Result<Nothing> {
            val errorMsg = throwable.message ?: "操作失败"
            Log.e(tag, "操作失败: $errorMsg", throwable)
            return Result.failure(throwable)
        }
    }

### 3.3 使用示例

#### 示例 1：AuthRemoteDataSource（优化后）

    package com.narutohuo.xindazhou.user.data.remote

    import com.narutohuo.xindazhou.common.network.BaseRemoteDataSource
    import com.narutohuo.xindazhou.common.network.ApiServiceFactory
    import com.narutohuo.xindazhou.user.data.model.LoginRequest
    import com.narutohuo.xindazhou.user.data.model.LoginResponse
    import com.narutohuo.xindazhou.user.data.api.AuthApi

    /**
     * 认证远程数据源
     *
     * 继承 BaseRemoteDataSource，自动获得：
     * - 统一的错误处理
     * - 自动日志记录
     * - 网络异常友好提示
     * - 线程自动切换
     */
    class AuthRemoteDataSource : BaseRemoteDataSource() {

        private val authApi: AuthApi = ApiServiceFactory.create()

        /**
         * 登录
         *
         * 无需传入 dataClass，使用 reified 泛型自动推断
         */
        suspend fun login(request: LoginRequest): Result<LoginResponse> {
            return executeRequest(
                request = { authApi.login(request) },
                errorMessage = "登录失败"
            )
        }

        /**
         * 注册
         */
        suspend fun register(request: RegisterRequest): Result<RegisterResponse> {
            return executeRequest(
                request = { authApi.register(request) },
                errorMessage = "注册失败"
            )
        }

        /**
         * 刷新Token（可空数据）
         */
        suspend fun refreshToken(refreshToken: String): Result<TokenResponse?> {
            return executeNullableRequest(
                request = { authApi.refreshToken(refreshToken) },
                errorMessage = "刷新Token失败"
            )
        }
    }

#### 示例 2：UserRemoteDataSource（优化后）

    package com.narutohuo.xindazhou.user.data.remote

    import com.narutohuo.xindazhou.common.network.BaseRemoteDataSource
    import com.narutohuo.xindazhou.common.network.ApiServiceFactory
    import com.narutohuo.xindazhou.user.data.model.UserInfo
    import com.narutohuo.xindazhou.user.data.api.UserApi

    /**
     * 用户远程数据源
     */
    class UserRemoteDataSource : BaseRemoteDataSource() {

        // 可以自定义日志标签
        override val tag: String = "UserRemoteDataSource"

        private val userApi: UserApi = ApiServiceFactory.create()

        /**
         * 获取用户信息
         */
        suspend fun getUserInfo(userId: String): Result<UserInfo> {
            return executeRequest(
                request = { userApi.getUserInfo(userId) },
                errorMessage = "获取用户信息失败"
            )
        }

        /**
         * 更新用户信息（可空数据）
         */
        suspend fun updateUserInfo(request: UpdateUserRequest): Result<UserInfo?> {
            return executeNullableRequest(
                request = { userApi.updateUserInfo(request) },
                errorMessage = "更新用户信息失败"
            )
        }
    }

#### 示例 3：AuthRepository（使用 BaseRepository）

    package com.narutohuo.xindazhou.user.data.repository

    import com.narutohuo.xindazhou.common.network.BaseRepository
    import com.narutohuo.xindazhou.user.data.remote.AuthRemoteDataSource
    import com.narutohuo.xindazhou.user.data.model.LoginRequest
    import com.narutohuo.xindazhou.user.data.model.LoginResponse

    /**
     * 认证仓库
     *
     * 继承 BaseRepository，自动获得：
     * - 统一的错误处理
     * - 日志记录
     * - 数据转换扩展点
     */
    class AuthRepository(
        private val remoteDataSource: AuthRemoteDataSource
    ) : BaseRepository() {

        /**
         * 登录
         */
        suspend fun login(mobile: String, password: String): Result<LoginResponse> {
            val request = LoginRequest(mobile, password)
            return remoteDataSource.login(request)
                .onFailure { throwable ->
                    // 可以在这里添加额外的错误处理逻辑
                    // 例如：记录日志、发送错误上报等
                    handleError(throwable)
                }
        }

        /**
         * 注册
         */
        suspend fun register(mobile: String, password: String, verifyCode: String): Result<RegisterResponse> {
            val request = RegisterRequest(mobile, password, verifyCode)
            return remoteDataSource.register(request)
        }
    }

### 3.4 ViewModel 层使用（保持不变）

    package com.narutohuo.xindazhou.user.ui.viewmodel

    import androidx.lifecycle.AndroidViewModel
    import androidx.lifecycle.viewModelScope
    import kotlinx.coroutines.launch
    import com.narutohuo.xindazhou.user.data.repository.AuthRepository

    class LoginViewModel(
        application: Application,
        private val authRepository: AuthRepository
    ) : AndroidViewModel(application) {

        private val _loginState = MutableStateFlow<LoginState>(LoginState.Idle)
        val loginState: StateFlow<LoginState> = _loginState

        fun login(mobile: String, password: String) {
            viewModelScope.launch {
                _loginState.value = LoginState.Loading

                authRepository.login(mobile, password)
                    .onSuccess {
                        _loginState.value = LoginState.Success("登录成功")
                    }
                    .onFailure { e ->
                        _loginState.value = LoginState.Error(e.message ?: "登录失败")
                    }
            }
        }
    }

## 四、目录结构

    base-common/src/main/java/com/narutohuo/xindazhou/common/
    ├── network/
    │   ├── ApiResponseParser.kt          # 响应解析器（已存在）
    │   ├── ApiServiceFactory.kt          # API服务工厂（已存在）
    │   ├── CommonResult.kt               # 通用响应模型（已存在）
    │   ├── BaseRemoteDataSource.kt       # 基础远程数据源（新增，增强版）
    │   └── BaseRepository.kt             # 基础仓库类（新增，可选）
    │
    └── ui/
        ├── BaseActivity.kt               # 基础Activity（新增）
        └── BaseFragment.kt               # 基础Fragment（新增）

    示例模块：
    user/
    ├── data/
    │   ├── remote/
    │   │   └── AuthRemoteDataSource.kt   # 认证远程数据源（继承BaseRemoteDataSource）
    │   └── repository/
    │       └── AuthRepository.kt         # 认证仓库（继承BaseRepository，可选）
    │
    └── ui/
        ├── login/
        │   ├── LoginActivity.kt          # 登录Activity（继承BaseActivity）
        │   └── LoginFragment.kt          # 登录Fragment（继承BaseFragment）
        └── viewmodel/
            └── LoginViewModel.kt         # 登录ViewModel

## 五、优化亮点

### 5.1 使用 reified 泛型，简化调用

- ❌ 之前：需要手动传入 `dataClass = LoginResponse::class.java`
- ✅ 现在：自动推断类型，无需传入 `dataClass`

### 5.2 统一公共处理

- ✅ 自动线程切换：所有请求自动切换到 IO 线程
- ✅ 统一错误处理：网络异常、超时等自动处理并给出友好提示
- ✅ 自动日志记录：请求开始、成功、失败自动记录日志
- ✅ 自定义日志标签：子类可重写 `tag` 属性自定义日志标签

### 5.3 网络异常友好提示

- `SocketTimeoutException` → "请求超时，请检查网络连接"
- `UnknownHostException` → "网络连接失败，请检查网络设置"
- 其他异常 → 显示具体错误信息

### 5.4 代码对比

优化前：

    suspend fun login(request: LoginRequest): Result<LoginResponse> {
        return try {
            val response = authApi.login(request)
            ApiResponseParser.handleResponse(
                response,
                LoginResponse::class.java,
                "登录失败"
            )
        } catch (e: Exception) {
            Result.failure(e)
        }
    }

优化后：

    suspend fun login(request: LoginRequest): Result<LoginResponse> {
        return executeRequest(
            request = { authApi.login(request) },
            errorMessage = "登录失败"
        )
    }

## 六、优势

✅ 代码简化：统一的网络请求处理，减少重复代码 70%+
✅ 错误统一：统一的错误处理逻辑，友好的错误提示
✅ 易于测试：DataSource 和 Repository 可以轻松 Mock
✅ 符合架构：完全符合 MVVM 四层架构设计
✅ 自动处理：线程切换、日志记录、异常处理全自动
✅ 类型安全：使用 reified 泛型，编译时类型检查

## 七、业务层基类设计

### 7.1 BaseActivity 基类

    package com.narutohuo.xindazhou.common.ui

    import android.os.Bundle
    import android.view.View
    import androidx.appcompat.app.AppCompatActivity
    import androidx.lifecycle.lifecycleScope
    import androidx.viewbinding.ViewBinding
    import kotlinx.coroutines.launch
    import android.widget.Toast

    /**
     * 基础 Activity
     *
     * 封装通用功能：
     * - ViewBinding 支持
     * - 统一的加载状态管理
     * - 统一的错误提示
     * - 生命周期管理
     *
     * 使用方式：
     * ```kotlin
     * class LoginActivity : BaseActivity<ActivityLoginBinding>() {
     *     override fun onCreate(savedInstanceState: Bundle?) {
     *         super.onCreate(savedInstanceState)
     *         // 你的代码
     *     }
     * }
     * ```
     */
    abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {

        protected lateinit var binding: VB
        protected abstract fun getViewBinding(): VB

        // 加载状态（子类可重写自定义实现）
        protected open fun showLoading() {
            // 默认实现：显示加载提示
            // 子类可重写自定义加载UI
        }

        protected open fun hideLoading() {
            // 默认实现：隐藏加载提示
            // 子类可重写自定义加载UI
        }

        // 错误提示（子类可重写自定义实现）
        protected open fun showError(message: String) {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        }

        protected open fun showSuccess(message: String) {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        }

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            binding = getViewBinding()
            setContentView(binding.root)
            initView()
            initObserver()
        }

        /**
         * 初始化视图（子类实现）
         */
        protected open fun initView() {}

        /**
         * 初始化观察者（子类实现）
         */
        protected open fun initObserver() {}

        /**
         * 统一执行网络请求
         *
         * 自动处理加载状态和错误提示
         */
        protected fun <T> executeRequest(
            request: suspend () -> Result<T>,
            onSuccess: (T) -> Unit,
            onError: ((String) -> Unit)? = null,
            showLoading: Boolean = true
        ) {
            lifecycleScope.launch {
                try {
                    if (showLoading) showLoading()

                    request()
                        .onSuccess { data ->
                            hideLoading()
                            onSuccess(data)
                        }
                        .onFailure { throwable ->
                            hideLoading()
                            val errorMsg = throwable.message ?: "操作失败"
                            if (onError != null) {
                                onError(errorMsg)
                            } else {
                                showError(errorMsg)
                            }
                        }
                } catch (e: Exception) {
                    hideLoading()
                    val errorMsg = e.message ?: "操作失败"
                    if (onError != null) {
                        onError(errorMsg)
                    } else {
                        showError(errorMsg)
                    }
                }
            }
        }
    }

### 7.2 BaseFragment 基类

    package com.narutohuo.xindazhou.common.ui

    import android.os.Bundle
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.Toast
    import androidx.fragment.app.Fragment
    import androidx.lifecycle.lifecycleScope
    import androidx.viewbinding.ViewBinding
    import kotlinx.coroutines.launch

    /**
     * 基础 Fragment
     *
     * 封装通用功能：
     * - ViewBinding 支持
     * - 统一的加载状态管理
     * - 统一的错误提示
     * - 生命周期管理
     *
     * 使用方式：
     * ```kotlin
     * class LoginFragment : BaseFragment<FragmentLoginBinding>() {
     *     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
     *         super.onViewCreated(view, savedInstanceState)
     *         // 你的代码
     *     }
     * }
     * ```
     */
    abstract class BaseFragment<VB : ViewBinding> : Fragment() {

        protected lateinit var binding: VB
        protected abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB

        // 加载状态（子类可重写自定义实现）
        protected open fun showLoading() {
            // 默认实现：显示加载提示
            // 子类可重写自定义加载UI
        }

        protected open fun hideLoading() {
            // 默认实现：隐藏加载提示
            // 子类可重写自定义加载UI
        }

        // 错误提示（子类可重写自定义实现）
        protected open fun showError(message: String) {
            Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
        }

        protected open fun showSuccess(message: String) {
            Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
        }

        override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
            binding = getViewBinding(inflater, container)
            return binding.root
        }

        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
            initView()
            initObserver()
        }

        /**
         * 初始化视图（子类实现）
         */
        protected open fun initView() {}

        /**
         * 初始化观察者（子类实现）
         */
        protected open fun initObserver() {}

        /**
         * 统一执行网络请求
         *
         * 自动处理加载状态和错误提示
         */
        protected fun <T> executeRequest(
            request: suspend () -> Result<T>,
            onSuccess: (T) -> Unit,
            onError: ((String) -> Unit)? = null,
            showLoading: Boolean = true
        ) {
            lifecycleScope.launch {
                try {
                    if (showLoading) showLoading()

                    request()
                        .onSuccess { data ->
                            hideLoading()
                            onSuccess(data)
                        }
                        .onFailure { throwable ->
                            hideLoading()
                            val errorMsg = throwable.message ?: "操作失败"
                            if (onError != null) {
                                onError(errorMsg)
                            } else {
                                showError(errorMsg)
                            }
                        }
                } catch (e: Exception) {
                    hideLoading()
                    val errorMsg = e.message ?: "操作失败"
                    if (onError != null) {
                        onError(errorMsg)
                    } else {
                        showError(errorMsg)
                    }
                }
            }
        }
    }

## 八、业务层使用示例

### 8.1 Activity 中使用（推荐使用 ViewModel）

    package com.narutohuo.xindazhou.user.ui.login

    import androidx.lifecycle.ViewModelProvider
    import com.narutohuo.xindazhou.common.ui.BaseActivity
    import com.narutohuo.xindazhou.user.databinding.ActivityLoginBinding
    import com.narutohuo.xindazhou.user.ui.viewmodel.LoginViewModel
    import com.narutohuo.xindazhou.user.ui.viewmodel.LoginViewModelFactory

    /**
     * 登录 Activity
     */
    class LoginActivity : BaseActivity<ActivityLoginBinding>() {

        private lateinit var viewModel: LoginViewModel

        override fun getViewBinding(): ActivityLoginBinding {
            return ActivityLoginBinding.inflate(layoutInflater)
        }

        override fun initView() {
            // 初始化 ViewModel
            viewModel = ViewModelProvider(
                this,
                LoginViewModelFactory(application)
            )[LoginViewModel::class.java]

            // 设置点击事件
            binding.btnLogin.setOnClickListener {
                val mobile = binding.etMobile.text.toString()
                val password = binding.etPassword.text.toString()
                viewModel.login(mobile, password)
            }
        }

        override fun initObserver() {
            // 观察登录状态
            lifecycleScope.launch {
                viewModel.loginState.collect { state ->
                    when (state) {
                        is LoginState.Idle -> {}
                        is LoginState.Loading -> showLoading()
                        is LoginState.Success -> {
                            hideLoading()
                            showSuccess(state.message)
                            // 跳转到主页
                        }
                        is LoginState.Error -> {
                            hideLoading()
                            showError(state.message)
                        }
                    }
                }
            }
        }
    }

### 8.2 Fragment 中使用（推荐使用 ViewModel）

    package com.narutohuo.xindazhou.user.ui.login

    import androidx.fragment.app.viewModels
    import com.narutohuo.xindazhou.common.ui.BaseFragment
    import com.narutohuo.xindazhou.user.databinding.FragmentLoginBinding
    import com.narutohuo.xindazhou.user.ui.viewmodel.LoginViewModel

    /**
     * 登录 Fragment
     */
    class LoginFragment : BaseFragment<FragmentLoginBinding>() {

        private val viewModel: LoginViewModel by viewModels {
            LoginViewModelFactory(requireActivity().application)
        }

        override fun getViewBinding(
            inflater: LayoutInflater,
            container: ViewGroup?
        ): FragmentLoginBinding {
            return FragmentLoginBinding.inflate(inflater, container, false)
        }

        override fun initView() {
            // 设置点击事件
            binding.btnLogin.setOnClickListener {
                val mobile = binding.etMobile.text.toString()
                val password = binding.etPassword.text.toString()
                viewModel.login(mobile, password)
            }
        }

        override fun initObserver() {
            // 观察登录状态
            lifecycleScope.launch {
                viewModel.loginState.collect { state ->
                    when (state) {
                        is LoginState.Idle -> {}
                        is LoginState.Loading -> showLoading()
                        is LoginState.Success -> {
                            hideLoading()
                            showSuccess(state.message)
                        }
                        is LoginState.Error -> {
                            hideLoading()
                            showError(state.message)
                        }
                    }
                }
            }
        }
    }

### 8.3 Activity 中直接使用 Repository（简单场景）

    package com.narutohuo.xindazhou.user.ui.login

    import com.narutohuo.xindazhou.common.ui.BaseActivity
    import com.narutohuo.xindazhou.user.databinding.ActivityLoginBinding
    import com.narutohuo.xindazhou.user.data.repository.AuthRepository

    /**
     * 登录 Activity（直接使用 Repository）
     *
     * 适用于简单的业务场景，不推荐用于复杂业务
     */
    class LoginActivity : BaseActivity<ActivityLoginBinding>() {

        private val authRepository = AuthRepository(...) // 通过依赖注入获取

        override fun getViewBinding(): ActivityLoginBinding {
            return ActivityLoginBinding.inflate(layoutInflater)
        }

        override fun initView() {
            binding.btnLogin.setOnClickListener {
                val mobile = binding.etMobile.text.toString()
                val password = binding.etPassword.text.toString()

                // 使用基类的 executeRequest 方法
                executeRequest(
                    request = { authRepository.login(mobile, password) },
                    onSuccess = { response ->
                        showSuccess("登录成功")
                        // 处理登录成功
                    },
                    onError = { errorMsg ->
                        // 默认会调用 showError，也可以自定义处理
                    }
                )
            }
        }
    }

## 九、完整架构层次

    业务层（UI层）
    ├── BaseActivity / BaseFragment          # 基础UI组件（新增）
    │   ├── ViewBinding 支持
    │   ├── 加载状态管理
    │   ├── 错误提示
    │   └── executeRequest() 便捷方法
    │
    ├── Activity / Fragment                  # 业务UI组件
    │   └── 继承 BaseActivity / BaseFragment
    │
    └── ViewModel                            # 视图模型
        └── 使用 Repository

    数据层
    ├── Repository                           # 数据仓库
    │   └── 继承 BaseRepository（可选）
    │
    ├── RemoteDataSource                     # 远程数据源
    │   └── 继承 BaseRemoteDataSource
    │
    └── ApiServiceFactory                    # API服务工厂

## 十、使用注意事项

1. 必须继承基类：

   - 所有 RemoteDataSource 必须继承 `BaseRemoteDataSource`
   - 所有 Activity 建议继承 `BaseActivity`
   - 所有 Fragment 建议继承 `BaseFragment`

2. 使用 executeRequest：

   - DataSource 层：使用 `executeRequest()` 处理非空数据
   - DataSource 层：使用 `executeNullableRequest()` 处理可空数据
   - Activity/Fragment 层：使用 `executeRequest()` 统一处理请求（如果直接使用 Repository）

3. 推荐使用 ViewModel：

   - 复杂业务推荐使用 ViewModel + StateFlow
   - 简单业务可以直接使用基类的 `executeRequest()` 方法

4. 自定义实现：

   - 可以重写 `showLoading()`, `hideLoading()`, `showError()` 等方法自定义UI
   - 可以重写 `tag` 属性自定义日志标签

5. CommonResult：已在 `ApiResponseParser.kt` 中定义，无需重复定义