refactor(network): 引入 RemoteRepository 隔离网络层,AppRepository 支持调料槽位查询

- 新增 RemoteRepository,将 NetViewModel 中直接调用 apiService 的逻辑下沉至仓库层
- NetViewModel 改为依赖注入 RemoteRepository,不再直接接触 apiService
- AppRepository 构造函数增加 SeasoningSlotDao 参数,新增 hasSeasoningSlotConfig() 方法
- AppViewModel 新增 hasSeasoningSlotConfig() 方法,明确区分"调料数据"与"槽位配置"语义
- 同步更新 6 个 Activity 的 AppFactory 构造调用,传入 seasoningSlotDao

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-22 18:31:38 +08:00
co-authored by Claude Sonnet 4.6
parent 0498501b33
commit 75830b9204
12 changed files with 172 additions and 54 deletions
@@ -2,6 +2,7 @@ package com.shuwei.dish.match.db
import com.shuwei.dish.match.base.BaseApp
import com.shuwei.dish.match.db.dao.AppDao
import com.shuwei.dish.match.db.dao.SeasoningSlotDao
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.SeasoningEntity
@@ -9,7 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class AppRepository(val appDao: AppDao) {
class AppRepository(val appDao: AppDao, val seasoningSlotDao: SeasoningSlotDao) {
suspend fun insertCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) {
appDao.insertCookFood(item)
@@ -105,6 +106,15 @@ class AppRepository(val appDao: AppDao) {
suspend fun insertSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) {
appDao.insertSeasoning(item)
}
/**
* 查询指定设备是否已配置调料槽位
* @param deviceId 目标设备 ID
* @return true 表示已有配置,false 表示未配置
*/
suspend fun hasSeasoningSlotConfig(deviceId: String) = withContext(Dispatchers.IO) {
seasoningSlotDao.queryByDeviceId(deviceId).isNotEmpty()
}
}
//
//class SeasoningRepository(val seasoningDao: SeasoningDao) {
@@ -47,6 +47,7 @@ import java.net.UnknownHostException
* @param onSuccess 成功回调
* @param onFailure 自定义错误回调(可选,不传则用默认提示)
*/
@Suppress("UNCHECKED_CAST")
suspend fun <T> request(
onRequest: suspend () -> ApiResponse<T>,
onSuccess: (T) -> Unit,
@@ -73,6 +74,7 @@ data class CodeMsg(
var msg: String? = null
)
@Suppress("DEPRECATION")
fun getApiException(e: Exception): ApiException {
// 捕获各类网络异常并转换为自定义异常
return when (e) {
@@ -116,7 +118,7 @@ fun httpException2ApiException(e: HttpException): ApiException {
val body = e.response()?.errorBody()?.string()
val codeMsg: CodeMsg? = try {
body?.toObject<CodeMsg>()
} catch (ex: Exception) {
} catch (_: Exception) {
null
}
return ApiException(
@@ -8,104 +8,89 @@ import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.entity.SeasoningEntity
import kotlinx.coroutines.launch
class NetViewModel : ViewModel() {
/**
* 网络请求 ViewModel,所有网络操作通过 RemoteRepository 发起,不直接依赖 apiService
* @param repository 网络数据仓库,默认使用单例实例
*/
class NetViewModel(
private val repository: RemoteRepository = RemoteRepository()
) : ViewModel() {
/**
* 提交制作菜品
*/
fun submitCookFood(
entity: CookFoodEntity,
onSuccess: (Any?) -> Unit,
onFailure: (String, String) -> Unit
) {
viewModelScope.launch {
request(
onRequest = {
apiService.submitCookFood(param = entity)
},
onSuccess = onSuccess,
onFailure = onFailure
)
repository.submitCookFood(entity, onSuccess, onFailure)
}
}
/**
* 查询菜品详情
*/
fun getFoodDetail(
foodId: String,
onSuccess: (CookFoodEntity?) -> Unit,
onFailure: (String, String) -> Unit
) {
viewModelScope.launch {
request(
onRequest = {
apiService.getFoodDetail(foodId = foodId)
},
onSuccess = onSuccess,
onFailure = onFailure
)
repository.getFoodDetail(foodId, onSuccess, onFailure)
}
}
/**
* 搜索菜品列表
*/
fun searchFoodList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) {
viewModelScope.launch {
request(
onRequest = {
apiService.searchFoodList(param = param)
},
onSuccess = onSuccess,
onFailure = onFailure
)
repository.searchFoodList(param, onSuccess, onFailure)
}
}
/**
* 查询采样数据列表
*/
fun getSamplingList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) {
viewModelScope.launch {
request(
onRequest = {
apiService.getSamplingList(param = param)
},
onSuccess = onSuccess,
onFailure = onFailure
)
repository.getSamplingList(param, onSuccess, onFailure)
}
}
/**
* 查询物品信息列表
*/
fun queryGoodsList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<CookFoodGoodsEntity>?) -> Unit,
onFailure: (String, String) -> Unit
) {
viewModelScope.launch {
request(
onRequest = {
apiService.queryGoodsList(param = param)
},
onSuccess = onSuccess,
onFailure = onFailure
)
repository.queryGoodsList(param, onSuccess, onFailure)
}
}
/**
* 查询调料信息列表
*/
fun querySeasoningList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<SeasoningEntity>?) -> Unit,
onFailure: (String, String) -> Unit
) {
viewModelScope.launch {
request(
onRequest = {
apiService.querySeasoningList(param = param)
},
onSuccess = onSuccess,
onFailure = onFailure
)
repository.querySeasoningList(param, onSuccess, onFailure)
}
}
}
@@ -0,0 +1,109 @@
package com.shuwei.dish.match.net
import com.shuwei.dish.match.entity.CookFoodEntity
import com.shuwei.dish.match.entity.CookFoodGoodsEntity
import com.shuwei.dish.match.entity.FoodRecord
import com.shuwei.dish.match.entity.SeasoningEntity
/**
* 网络数据仓库,封装所有 ApiService 调用
* ViewModel 不直接接触 apiService,统一通过此类发起网络请求
*/
class RemoteRepository {
/**
* 提交制作菜品
* @param entity 菜品实体
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
*/
suspend fun submitCookFood(
entity: CookFoodEntity,
onSuccess: (Any?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.submitCookFood(param = entity) },
onSuccess = onSuccess,
onFailure = onFailure
)
/**
* 查询菜品详情
* @param foodId 菜品 ID
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
*/
suspend fun getFoodDetail(
foodId: String,
onSuccess: (CookFoodEntity?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.getFoodDetail(foodId = foodId) },
onSuccess = onSuccess,
onFailure = onFailure
)
/**
* 搜索菜品列表
* @param param 查询参数
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
*/
suspend fun searchFoodList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.searchFoodList(param = param) },
onSuccess = onSuccess,
onFailure = onFailure
)
/**
* 查询采样数据列表
* @param param 查询参数
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
*/
suspend fun getSamplingList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<FoodRecord>?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.getSamplingList(param = param) },
onSuccess = onSuccess,
onFailure = onFailure
)
/**
* 查询物品信息列表
* @param param 查询参数
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
*/
suspend fun queryGoodsList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<CookFoodGoodsEntity>?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.queryGoodsList(param = param) },
onSuccess = onSuccess,
onFailure = onFailure
)
/**
* 查询调料信息列表
* @param param 查询参数
* @param onSuccess 成功回调
* @param onFailure 失败回调,参数为 (errorCode, errorMsg)
*/
suspend fun querySeasoningList(
param: MutableMap<String, Any>,
onSuccess: (MutableList<SeasoningEntity>?) -> Unit,
onFailure: (String, String) -> Unit
) = request(
onRequest = { apiService.querySeasoningList(param = param) },
onSuccess = onSuccess,
onFailure = onFailure
)
}
@@ -102,7 +102,7 @@ class DishSamplingActivity : BaseActivity() {
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory =
AppFactory(AppRepository(db.appDao()))
AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
appViewModel =
ViewModelProvider(this, factory)[AppViewModel::class.java]
}
@@ -138,7 +138,7 @@ class HomeActivity : BaseActivity() {
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory = AppFactory(AppRepository(db.appDao()))
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
@@ -70,7 +70,7 @@ class InitActivity : BaseActivity() {
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory = AppFactory(AppRepository(db.appDao()))
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
@@ -74,7 +74,7 @@ class SamplingListActivity : BaseActivity() {
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory = AppFactory(AppRepository(db.appDao()))
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
@@ -68,7 +68,7 @@ class SelectDishActivity : BaseActivity() {
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory = AppFactory(AppRepository(db.appDao()))
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
@@ -395,7 +395,7 @@ class SubmitFoodActivity : BaseActivity() {
private fun initViewModel() {
val db = BaseApp.instance!!.database
val factory = AppFactory(AppRepository(db.appDao()))
val factory = AppFactory(AppRepository(db.appDao(), db.seasoningSlotDao()))
appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java]
}
@@ -173,6 +173,17 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() {
}
}
/**
* 查询指定设备是否已配置调料槽位
* @param deviceId 目标设备 ID
* @param action 回调,true 表示已配置,false 表示未配置
*/
fun hasSeasoningSlotConfig(deviceId: String, action: (Boolean) -> Unit) {
viewModelScope.launch {
action(rep.hasSeasoningSlotConfig(deviceId))
}
}
// fun updateAll(list: MutableList<SeasoningEntity>) {
// viewModelScope.launch {
// list.forEach {
@@ -3,6 +3,7 @@ package com.shuwei.dish.match.viewmodel.factory
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.shuwei.dish.match.db.AppRepository
import com.shuwei.dish.match.db.dao.SeasoningSlotDao
import com.shuwei.dish.match.viewmodel.AppViewModel
@Suppress("UNCHECKED_CAST")