From 30f6c74a994a45d6097bf6ee238c7124912354ac Mon Sep 17 00:00:00 2001 From: lvmeng <848755140@qq.com> Date: Thu, 21 May 2026 18:21:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=97=A7=E7=89=88=E9=85=8D=E6=AF=94=E7=A7=A4?= =?UTF-8?q?=E8=BF=81=E7=A7=BB=E6=96=B0=E7=89=88=E6=8E=A5=E5=8F=A3=E4=B8=8E?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 net 包:ApiService、RemoteRepository、NetViewModel、UiState、ErrorType 等,替换旧 http 包 - 新增 model 包:CookFoodDTO、GoodsItem、FoodRecord 等 UI 层数据模型 - 重构 db 层:新增 DbRepository、DbViewModel,更新 Room Entity 和 DAO,升级数据库版本至13 - 删除旧 entity 包、http 包、viewmodel/factory 包 - 适配器(DishPartAdapter、GoodsInfoSearchAdapter、SeasoningSearchAdapter)改用 model 类,不再依赖数据库实体 - BaseActivity 统一通过 by viewModels() 提供 appViewModel/netViewModel/dbViewModel,移除各 Activity 工厂模式初始化 - 新增 GlobalData、ResetReasoningRecord;ErrorType 补充 getApiException 函数;build.gradle 添加 Retrofit2 依赖 --- app/build.gradle.kts | 3 + .../dish/match/adapter/DishPartAdapter.kt | 24 +- .../dish/match/adapter/DishShowAdapter.kt | 2 +- .../dish/match/adapter/FoodRecordAdapter.kt | 2 +- .../match/adapter/GoodsInfoSearchAdapter.kt | 10 +- .../dish/match/adapter/HomeModeAdapter.kt | 2 +- .../dish/match/adapter/SamplingAdapter.kt | 2 +- .../dish/match/adapter/SeasoningAdapter.kt | 41 ++ .../dish/match/adapter/SeasoningCellTool.kt | 2 +- .../match/adapter/SeasoningSearchAdapter.kt | 10 +- .../dish/match/adapter/TextCellAdapter.kt | 2 +- .../shuwei/dish/match/base/BaseActivity.kt | 10 +- .../com/shuwei/dish/match/base/GlobalData.kt | 39 ++ .../com/shuwei/dish/match/db/AppDatabase.kt | 88 ++- .../dish/match/db/DatabaseRepository.kt | 142 ---- .../com/shuwei/dish/match/db/DbRepository.kt | 315 +++++++++ .../com/shuwei/dish/match/db/DbViewModel.kt | 623 ++++++++++++++++++ .../com/shuwei/dish/match/db/dao/AppDao.kt | 92 --- .../shuwei/dish/match/db/dao/CookFoodDao.kt | 81 ++- .../dish/match/db/dao/CookFoodGoodsDao.kt | 90 ++- .../shuwei/dish/match/db/dao/SeasoningDao.kt | 95 ++- .../dish/match/db/dao/SeasoningSlotDao.kt | 77 +++ .../match/{ => db}/entity/CookFoodEntity.kt | 12 +- .../{ => db}/entity/CookFoodGoodsEntity.kt | 57 +- .../match/db/entity/ResetReasoningRecord.kt | 6 + .../match/{ => db}/entity/SeasoningEntity.kt | 38 +- .../match/db/entity/SeasoningSlotEntity.kt | 23 + .../shuwei/dish/match/dialog/BottomDialog.kt | 102 ++- .../shuwei/dish/match/dialog/BottomDialog2.kt | 101 ++- .../com/shuwei/dish/match/entity/DataBean.kt | 62 -- .../shuwei/dish/match/entity/DishEntity.kt | 25 - .../dish/match/entity/GoodsInfoEntity.kt | 38 -- .../com/shuwei/dish/match/http/HttpUtil.kt | 199 ------ .../com/shuwei/dish/match/http/UrlConfig.kt | 36 - .../dish/match/model/CollectedFoodItem.kt | 11 + .../shuwei/dish/match/model/CookFoodDTO.kt | 43 ++ .../dish/match/model/CookFoodGoodsDTO.kt | 119 ++++ .../com/shuwei/dish/match/model/DataBean.kt | 52 ++ .../match/{entity => model}/FoodRecord.kt | 6 +- .../com/shuwei/dish/match/model/GoodsItem.kt | 83 +++ .../dish/match/model/GoodsNameQueryDTO.kt | 12 + .../shuwei/dish/match/model/SettingItem.kt | 40 ++ .../com/shuwei/dish/match/net/ApiService.kt | 76 +++ .../com/shuwei/dish/match/net/ErrorType.kt | 34 + .../dish/match/net/ExceptionInterceptor.kt | 40 ++ .../HttpClient.kt => net/HttpManager.kt} | 68 +- .../com/shuwei/dish/match/net/NetViewModel.kt | 258 ++++++++ .../shuwei/dish/match/net/RemoteRepository.kt | 53 ++ .../dish/match/net/RequestInterceptor.kt | 22 + .../com/shuwei/dish/match/net/RespData.kt | 9 + .../java/com/shuwei/dish/match/net/UiState.kt | 15 + .../dish/match/ui/DeviceConfigActivity.kt | 29 +- .../dish/match/ui/DishSamplingActivity.kt | 40 +- .../dish/match/ui/FoodSearchActivity.kt | 93 ++- .../com/shuwei/dish/match/ui/HomeActivity.kt | 19 +- .../com/shuwei/dish/match/ui/InitActivity.kt | 87 +-- .../dish/match/ui/PrepareCookActivity.kt | 92 +-- .../dish/match/ui/SamplingListActivity.kt | 84 +-- .../dish/match/ui/SelectDishActivity.kt | 19 +- .../dish/match/ui/SubmitFoodActivity.kt | 93 ++- .../match/ui/fragment/DishListFragment.kt | 73 +- .../dish/match/viewmodel/AppViewModel.kt | 102 +-- .../match/viewmodel/CookFoodGoodsViewModel.kt | 2 +- .../dish/match/viewmodel/CookFoodViewModel.kt | 2 +- .../match/viewmodel/DeviceSettingViewModel.kt | 2 +- .../match/viewmodel/SubmitDishViewModel.kt | 4 +- .../dish/match/viewmodel/factory/VMFactory.kt | 43 -- 67 files changed, 2739 insertions(+), 1437 deletions(-) create mode 100644 app/src/main/java/com/shuwei/dish/match/adapter/SeasoningAdapter.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/base/GlobalData.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/db/DbRepository.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningSlotDao.kt rename app/src/main/java/com/shuwei/dish/match/{ => db}/entity/CookFoodEntity.kt (64%) rename app/src/main/java/com/shuwei/dish/match/{ => db}/entity/CookFoodGoodsEntity.kt (52%) create mode 100644 app/src/main/java/com/shuwei/dish/match/db/entity/ResetReasoningRecord.kt rename app/src/main/java/com/shuwei/dish/match/{ => db}/entity/SeasoningEntity.kt (59%) create mode 100644 app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningSlotEntity.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/model/CollectedFoodItem.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/model/CookFoodDTO.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/model/CookFoodGoodsDTO.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/model/DataBean.kt rename app/src/main/java/com/shuwei/dish/match/{entity => model}/FoodRecord.kt (78%) create mode 100644 app/src/main/java/com/shuwei/dish/match/model/GoodsItem.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/model/GoodsNameQueryDTO.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/model/SettingItem.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/ApiService.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/ErrorType.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/ExceptionInterceptor.kt rename app/src/main/java/com/shuwei/dish/match/{http/HttpClient.kt => net/HttpManager.kt} (50%) create mode 100644 app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/RemoteRepository.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/RequestInterceptor.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/RespData.kt create mode 100644 app/src/main/java/com/shuwei/dish/match/net/UiState.kt delete mode 100644 app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6302638..0b56f11 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -88,6 +88,9 @@ dependencies { androidTestImplementation(libs.androidx.espresso.core) implementation("com.squareup.okhttp3:okhttp:4.9.1") + implementation("com.squareup.okhttp3:logging-interceptor:4.9.1") + implementation("com.squareup.retrofit2:retrofit:2.9.0") + implementation("com.squareup.retrofit2:converter-gson:2.9.0") implementation("com.google.code.gson:gson:2.10.1") // implementation("com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.30") diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt index c20ecc3..923dd03 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/DishPartAdapter.kt @@ -8,12 +8,11 @@ import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R import com.shuwei.dish.match.databinding.ListItemDishCookBinding -import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.model.GoodsItem import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace -import java.text.DecimalFormat -class DishPartAdapter(list: MutableList) : - BaseQuickAdapter(list) { +class DishPartAdapter(list: MutableList) : + BaseQuickAdapter(list) { inner class VH(var binding: ListItemDishCookBinding) : QuickViewHolder(binding.root) @@ -23,11 +22,10 @@ class DishPartAdapter(list: MutableList) : return VH(binding) } - override fun onBindViewHolder(holder: VH, position: Int, item: CookFoodGoodsEntity?) { + override fun onBindViewHolder(holder: VH, position: Int, item: GoodsItem?) { holder.binding.run { tvDishName.text = item!!.goodsName tvDishType.text = if (item.materialType == 1) "主辅材:主材" else if (item.materialType == 2) "主辅材:辅材" else "" - //"${DecimalFormat("#").format(item.useWeight)}克" tvDishWeight.text = if (item.useWeight == null || item.useWeight == 0.toDouble()) "" else "${item.useWeight!!.roundedOneDecimalPlace()}克" tvDishWeight.setTextColor( @@ -38,20 +36,16 @@ class DishPartAdapter(list: MutableList) : ) ivOperateIcon.setImageResource( if (item.isSamplingPage) - //采集页面 R.drawable.ic_delete else - //制作页面 if (item.isSetFinished) R.drawable.ic_dish_selected else R.drawable.ic_dish_unselected ) - root.run { - setBackgroundResource( - if (item.isItemClicked) R.drawable.shape_item_cook_dish - else R.drawable.shape_white_fb_15_corners - ) - } + root.setBackgroundResource( + if (item.isClicked) R.drawable.shape_item_cook_dish + else R.drawable.shape_white_fb_15_corners + ) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt index 7fd7dab..ca389e3 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/DishShowAdapter.kt @@ -7,7 +7,7 @@ import androidx.core.content.ContextCompat import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R -import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.model.FoodRecord import com.shuwei.dish.match.databinding.ListItemDishBinding import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.visible diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt index 7f6e7fd..763e252 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/FoodRecordAdapter.kt @@ -6,7 +6,7 @@ import android.view.ViewGroup import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.databinding.ListItemDishRecordBinding -import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.model.FoodRecord class FoodRecordAdapter(list: MutableList) : BaseQuickAdapter(list) { diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt index c4623f1..3838ca7 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/GoodsInfoSearchAdapter.kt @@ -8,15 +8,15 @@ import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding -import com.shuwei.dish.match.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.model.GoodsItem -class GoodsInfoSearchAdapter(private var list: MutableList) : - BaseQuickAdapter(list) { +class GoodsInfoSearchAdapter(private var list: MutableList) : + BaseQuickAdapter(list) { override fun onBindViewHolder( holder: VH, position: Int, - item: CookFoodGoodsEntity? + item: GoodsItem? ) { holder.binding.tvGoodsInfo.run { text = item?.goodsName @@ -46,4 +46,4 @@ class GoodsInfoSearchAdapter(private var list: MutableList) inner class VH(var binding: ListItemSearchGoodsInfoBinding) : QuickViewHolder(binding.root) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt index 233651d..c117ac5 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/HomeModeAdapter.kt @@ -8,7 +8,7 @@ import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R import com.shuwei.dish.match.databinding.ListItemHomeModeBinding -import com.shuwei.dish.match.entity.HomeModeBean +import com.shuwei.dish.match.model.HomeModeBean import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.visible diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt index 3ae27b6..5019839 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SamplingAdapter.kt @@ -7,7 +7,7 @@ import androidx.core.content.ContextCompat import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R -import com.shuwei.dish.match.entity.FoodRecord +import com.shuwei.dish.match.model.FoodRecord import com.shuwei.dish.match.databinding.ListItemDishBinding import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.visible diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningAdapter.kt new file mode 100644 index 0000000..51398fb --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningAdapter.kt @@ -0,0 +1,41 @@ +package com.shuwei.dish.match.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.core.view.updateLayoutParams +import com.chad.library.adapter4.BaseQuickAdapter +import com.chad.library.adapter4.viewholder.QuickViewHolder +import com.shuwei.dish.match.databinding.ListItemCellBinding +import com.shuwei.dish.match.db.entity.SeasoningEntity +import com.shuwei.dish.match.utils.ext.dp + +/** + * 调料格子列表适配器,使用 list_item_cell 布局展示调料名称与用量 + * 第4个格子(position=3)为大格,其余为标准格 + */ +class SeasoningAdapter(list: MutableList) : + BaseQuickAdapter(list) { + + override fun onCreateViewHolder(context: Context, parent: ViewGroup, viewType: Int): VH { + val binding = ListItemCellBinding.inflate(LayoutInflater.from(context), parent, false) + return VH(binding) + } + + override fun onBindViewHolder(holder: VH, position: Int, item: SeasoningEntity?) { + item ?: return + // 第4个格子(index=3)使用大尺寸,其余使用标准尺寸 + val isLarge = position == 3 + holder.binding.root.updateLayoutParams { + width = if (isLarge) 467.dp else 226.dp + height = if (isLarge) 255.dp else 120.dp + leftMargin = 7.dp + rightMargin = 8.dp + topMargin = 7.dp + bottomMargin = 8.dp + } + TextCellAdapter.loadCell(holder.binding.tvCell, item) + } + + inner class VH(val binding: ListItemCellBinding) : QuickViewHolder(binding.root) +} diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt index f024d41..7a331e2 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningCellTool.kt @@ -6,7 +6,7 @@ //import androidx.recyclerview.widget.RecyclerView //import com.shuwei.dish.match.base.BaseActivity //import com.shuwei.dish.match.dialog.BottomDialog2 -//import com.shuwei.dish.match.entity.SeasoningEntity +//import com.shuwei.dish.match.db.entity.SeasoningEntity //import com.shuwei.dish.match.ui.DeviceSettingActivity //import com.shuwei.dish.match.ui.SubmitDishActivity //import com.shuwei.dish.match.utils.DataUtil diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt index 1477df3..2dc7ac0 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/SeasoningSearchAdapter.kt @@ -8,15 +8,15 @@ import com.chad.library.adapter4.BaseQuickAdapter import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R import com.shuwei.dish.match.databinding.ListItemSearchGoodsInfoBinding -import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.model.GoodsItem -class SeasoningSearchAdapter(private var list: MutableList) : - BaseQuickAdapter(list) { +class SeasoningSearchAdapter(private var list: MutableList) : + BaseQuickAdapter(list) { override fun onBindViewHolder( holder: VH, position: Int, - item: SeasoningEntity? + item: GoodsItem? ) { holder.binding.tvGoodsInfo.run { text = item?.goodsName @@ -46,4 +46,4 @@ class SeasoningSearchAdapter(private var list: MutableList) : inner class VH(var binding: ListItemSearchGoodsInfoBinding) : QuickViewHolder(binding.root) -} \ No newline at end of file +} diff --git a/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt b/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt index 58c90b6..7f77e67 100644 --- a/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt +++ b/app/src/main/java/com/shuwei/dish/match/adapter/TextCellAdapter.kt @@ -17,7 +17,7 @@ import androidx.recyclerview.widget.RecyclerView import com.chad.library.adapter4.viewholder.QuickViewHolder import com.shuwei.dish.match.R import com.shuwei.dish.match.databinding.ListItemCellBinding -import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.db.entity.SeasoningEntity import com.shuwei.dish.match.utils.ext.appendText import com.shuwei.dish.match.utils.ext.buildSpannableString import com.shuwei.dish.match.utils.ext.clickWithDebounce diff --git a/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt b/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt index f309081..934b6f4 100644 --- a/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/base/BaseActivity.kt @@ -12,13 +12,15 @@ import android.widget.TextView import androidx.activity.enableEdgeToEdge import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts +import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat import com.shuwei.dish.match.R import com.shuwei.dish.match.databinding.ActivityBaseBinding +import com.shuwei.dish.match.db.DbViewModel import com.shuwei.dish.match.dialog.Loading -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.net.NetViewModel +import com.shuwei.dish.match.viewmodel.AppViewModel import com.shuwei.dish.match.ui.HomeActivity.Companion.TAG import com.shuwei.dish.match.ui.InitActivity import com.shuwei.dish.match.utils.ActivityManager @@ -35,6 +37,10 @@ import java.util.Locale @Suppress("DEPRECATION") open class BaseActivity : AppCompatActivity() { + val netViewModel: NetViewModel by viewModels() + val dbViewModel: DbViewModel by viewModels() + val appViewModel: AppViewModel by viewModels() + private lateinit var binding: ActivityBaseBinding private var launcher: ActivityResultLauncher? = null private var launchCallback: ((Intent?) -> Unit)? = null diff --git a/app/src/main/java/com/shuwei/dish/match/base/GlobalData.kt b/app/src/main/java/com/shuwei/dish/match/base/GlobalData.kt new file mode 100644 index 0000000..685a081 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/base/GlobalData.kt @@ -0,0 +1,39 @@ +package com.shuwei.dish.match.base + +/** + * 设备角色枚举 + * MASTER:主设备,负责汇聚所有设备的秤数据并展示大屏界面 + * SLAVE:子设备,负责推送本机秤数据给主设备 + */ +enum class DeviceRole { MASTER, SLAVE } + +object GlobalData { + var appBaseUrl: String = TEST_BASE_URL + + /** + * 具体业务 BaseUrl + */ + const val TEST_BASE_URL = "http://192.168.1.201:14801" + const val UAT_BASE_URL = "https://dev.yixiong-tech.com:8083" + const val PROD_BASE_URL = "https://api.dm.yixiong-tech.com:8443" + /** + * 设备id + */ + var deviceId: String = "" + + /** + * 当前设备角色,启动时从 SpTool 读取,默认为 SLAVE + */ + var deviceRole: DeviceRole = DeviceRole.SLAVE +} + +/** + * + */ +object GlobalKey { + const val KEY_EQUIPMENT_INFO = "equipmentInfo" + const val KEY_TOKEN = "tokenKey" + const val KEY_USER_INFO = "userInfoKey" + const val KEY_USER_NAME = "userNameKey" + const val KEY_BASE_URL = "baseUrl" +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt b/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt index 319bf8c..8f21ec4 100644 --- a/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/AppDatabase.kt @@ -6,10 +6,14 @@ import androidx.room.Room import androidx.room.RoomDatabase import androidx.room.migration.Migration import androidx.sqlite.db.SupportSQLiteDatabase -import com.shuwei.dish.match.db.dao.AppDao -import com.shuwei.dish.match.entity.CookFoodEntity -import com.shuwei.dish.match.entity.CookFoodGoodsEntity -import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.db.dao.CookFoodDao +import com.shuwei.dish.match.db.dao.CookFoodGoodsDao +import com.shuwei.dish.match.db.dao.SeasoningDao +import com.shuwei.dish.match.db.dao.SeasoningSlotDao +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.db.entity.SeasoningEntity +import com.shuwei.dish.match.db.entity.SeasoningSlotEntity // 步骤1:更新版本号 @@ -19,16 +23,20 @@ import com.shuwei.dish.match.entity.SeasoningEntity SeasoningEntity::class, CookFoodEntity::class, CookFoodGoodsEntity::class, + SeasoningSlotEntity::class, ], - version = 7, + version = 13, exportSchema = true ) abstract class AppDatabase : RoomDatabase() { -// abstract fun seasoningDao(): SeasoningDao -// abstract fun cookFoodDao(): CookFoodDao -// abstract fun cookFoodGoodsDao(): CookFoodGoodsDao - abstract fun appDao(): AppDao + abstract fun cookFoodDao(): CookFoodDao + + abstract fun cookFoodGoodsDao(): CookFoodGoodsDao + + abstract fun seasoningDao(): SeasoningDao + + abstract fun seasoningSlotDao(): SeasoningSlotDao } @@ -140,6 +148,20 @@ val MIGRATION_5_6 = MigrationImpl(5, 6) { db -> db.endTransaction() } +val MIGRATION_7_8 = MigrationImpl(7, 8) { db -> + db.execSQL( + """ + CREATE TABLE IF NOT EXISTS `dm_seasoning_slot` ( + `deviceId` TEXT NOT NULL, + `address` INTEGER NOT NULL, + `goodsId` TEXT NOT NULL, + `goodsName` TEXT NOT NULL, + PRIMARY KEY(`deviceId`, `address`) + ) + """.trimIndent() + ) +} + val MIGRATION_6_7 = MigrationImpl(6, 7) {db -> runCatching { @@ -192,6 +214,48 @@ val MIGRATION_6_7 = MigrationImpl(6, 7) {db -> } } +/** + * 版本8→9:为 dm_cook_food_goods 和 dm_seasoning 表新增 zjmCode、materId 字段 + */ +val MIGRATION_8_9 = MigrationImpl(8, 9) { db -> + db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN zjmCode TEXT DEFAULT ''") + db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN materId TEXT DEFAULT ''") + db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN zjmCode TEXT DEFAULT ''") + db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN materId TEXT DEFAULT ''") +} + +/** + * 版本9→10:删除 dm_cook_food 表 foodId 唯一索引 + * 改为由应用层保证同一 foodId 且 isDel=0 只有一条记录,以支持逻辑删除 + */ +val MIGRATION_9_10 = MigrationImpl(9, 10) { db -> + db.execSQL("DROP INDEX IF EXISTS dm_cook_food_foodId_IDX") +} + +/** + * 版本10→11:为 dm_cook_food_goods 和 dm_seasoning 表新增 goodsCode(物料编码)字段 + */ +val MIGRATION_10_11 = MigrationImpl(10, 11) { db -> + db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN goodsCode TEXT DEFAULT ''") + db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN goodsCode TEXT DEFAULT ''") +} + +/** + * 版本11→12:为 dm_cook_food_goods 和 dm_seasoning 表新增 rawMaterialsType(净材种类)字段 + */ +val MIGRATION_11_12 = MigrationImpl(11, 12) { db -> + db.execSQL("ALTER TABLE dm_cook_food_goods ADD COLUMN rawMaterialsType TEXT DEFAULT NULL") + db.execSQL("ALTER TABLE dm_seasoning ADD COLUMN rawMaterialsType TEXT DEFAULT NULL") +} + +/** + * 版本12→13:为 dm_cook_food 表新增 submitState(接口提交状态)字段 + * 0-未提交(本地草稿/手动删除),1-接口提交成功后删除 + */ +val MIGRATION_12_13 = MigrationImpl(12, 13) { db -> + db.execSQL("ALTER TABLE dm_cook_food ADD COLUMN submitState INTEGER NOT NULL DEFAULT 0") +} + class DatabaseProvider(private val context: Context) { // 单例模式确保全局唯一实例‌ val instance: AppDatabase by lazy { @@ -210,6 +274,12 @@ class DatabaseProvider(private val context: Context) { // .addMigrations(MIGRATION_4_5) // .addMigrations(MIGRATION_5_6) // .addMigrations(MIGRATION_6_7) + .addMigrations(MIGRATION_7_8) + .addMigrations(MIGRATION_8_9) + .addMigrations(MIGRATION_9_10) + .addMigrations(MIGRATION_10_11) + .addMigrations(MIGRATION_11_12) + .addMigrations(MIGRATION_12_13) .build() } } diff --git a/app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt b/app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt deleted file mode 100644 index 7c293d2..0000000 --- a/app/src/main/java/com/shuwei/dish/match/db/DatabaseRepository.kt +++ /dev/null @@ -1,142 +0,0 @@ -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.entity.CookFoodEntity -import com.shuwei.dish.match.entity.CookFoodGoodsEntity -import com.shuwei.dish.match.entity.SeasoningEntity -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - - -class AppRepository(val appDao: AppDao) { - - suspend fun insertCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) { - appDao.insertCookFood(item) - } - - suspend fun updateCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) { - appDao.updateCookFood(item) - } - - suspend fun getCookFoodById(foodId: String, cookMode: Int) = withContext(Dispatchers.IO) { - appDao.getCookFoodById(BaseApp.canteenId, foodId, cookMode) - } - - suspend fun getCookFoodList(cookMode: Int, dinnerType: String) = withContext(Dispatchers.IO) { - appDao.getCookFoodList(BaseApp.canteenId, cookMode, dinnerType) - } - - suspend fun countCookFood(cookMode: Int) = withContext(Dispatchers.IO) { - appDao.countCookFood(BaseApp.canteenId, cookMode) - } - - suspend fun deleteCookFood(foodId: String, cookMode: Int) = withContext(Dispatchers.IO) { - appDao.deleteCookFood(BaseApp.canteenId, foodId, cookMode) - } - - suspend fun deleteCookFoodGoodsList(pid:Long, foodId: String) = withContext(Dispatchers.IO) { - appDao.deleteCookFoodGoodsList(pid, foodId) - } - //-------------------------------------------------------------------------------------------- - -// suspend fun insertGoods(item: CookFoodGoodsEntity) = withContext(Dispatchers.IO) { -// appDao.insertGoods(item) -// } - - suspend fun insertGoodsList(items: MutableList) = - withContext(Dispatchers.IO) { - appDao.insertGoodsList(items) - } - -// suspend fun updateGoods(item: CookFoodGoodsEntity) = withContext(Dispatchers.IO) { -// appDao.updateGoods(item) -// } - - suspend fun getCookFoodGoodsList(pid:Long, foodId: String) = withContext(Dispatchers.IO) { - appDao.getCookFoodGoodsList(pid, foodId) - } - - suspend fun getCookFoodGoodsListByType( - foodId: String, - materialType: Int - ) = withContext(Dispatchers.IO) { - appDao.getCookFoodGoodsListByType(foodId, materialType) - } - //-------------------------------------------------------------------------------------------- - - suspend fun getSeasoningById(id: Long) = withContext(Dispatchers.IO) { - appDao.getSeasoningById(id) - } - - suspend fun getSeasoningBySort(sort:Int) = withContext(Dispatchers.IO) { - appDao.getSeasoningBySort(sort) - } - - suspend fun getSeasoningByGoodsId(goodsId: Int) = withContext(Dispatchers.IO) { - appDao.getSeasoningByGoodsId(goodsId) - } - - suspend fun getAllStream() = withContext(Dispatchers.IO) { - appDao.getAllStream() - } - - suspend fun search(query: String) = withContext(Dispatchers.IO) { - appDao.search(query) - } - - suspend fun clearAllSeasoning() = withContext(Dispatchers.IO) { - appDao.clearAllSeasoning() - } - - suspend fun deleteSeasoningBySort(sort: Int) = withContext(Dispatchers.IO) { - appDao.deleteSeasoningBySort(sort) - } - - suspend fun insertSeasoningList(items: MutableList) = - withContext(Dispatchers.IO) { - appDao.insertSeasoningList(items) - } - - suspend fun updateSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { - appDao.updateSeasoning(item) - } - - suspend fun insertSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { - appDao.insertSeasoning(item) - } -} -// -//class SeasoningRepository(val seasoningDao: SeasoningDao) { -// val seasoningList: Flow> = seasoningDao.getAllStream() -// -// fun addSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { -// seasoningDao.insert(item) -// } -// -// fun searchSeasoning(query: String) = withContext(Dispatchers.IO) { -// seasoningDao.search(query) -// } -//} -// -//class CookFoodRepository(val cookFoodDao: CookFoodDao) { -// fun getCookFood(foodId: String, cookMode:Int = 0) = withContext(Dispatchers.IO) { -// cookFoodDao.getCookFoodById(foodId, cookMode) -// } -// -// fun getCookFoodByList(cookMode:Int = 0) = withContext(Dispatchers.IO) { -// cookFoodDao.getCookFoodList(cookMode) -// } -// -// fun countCookFood(cookMode:Int = 0)= withContext(Dispatchers.IO) { -// cookFoodDao.countCookFood(cookMode) -// } -//} -// -//class CookFoodGoodsRepository(val cookFoodGoodsDao: CookFoodGoodsDao) { -// fun getFoodGoodsList(foodId: String) = withContext(Dispatchers.IO) { -// cookFoodGoodsDao.getCookFoodGoodsList(foodId) -// } -// -//} - diff --git a/app/src/main/java/com/shuwei/dish/match/db/DbRepository.kt b/app/src/main/java/com/shuwei/dish/match/db/DbRepository.kt new file mode 100644 index 0000000..a049997 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/DbRepository.kt @@ -0,0 +1,315 @@ +package com.shuwei.dish.match.db + +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.db.entity.SeasoningEntity +import com.shuwei.dish.match.db.entity.SeasoningSlotEntity +import androidx.sqlite.db.SupportSQLiteQuery +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + + +class DbRepository { + + private val db = BaseApp.instance!!.database + + suspend fun insertCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) { + db.cookFoodDao().insertCookFood(item) + } + + suspend fun updateCookFood(item: CookFoodEntity) = withContext(Dispatchers.IO) { + db.cookFoodDao().updateCookFood(item) + } + + suspend fun getCookFoodById(foodId: String, cookMode: Int, dinnerType: String) = withContext(Dispatchers.IO) { + db.cookFoodDao().getCookFoodById(BaseApp.canteenId, foodId, cookMode, dinnerType) + } + + suspend fun getCookFoodList(cookMode: Int, dinnerType: String) = withContext(Dispatchers.IO) { + db.cookFoodDao().getCookFoodList(BaseApp.canteenId, cookMode, dinnerType) + } + + suspend fun countCookFood(cookMode: Int) = withContext(Dispatchers.IO) { + db.cookFoodDao().countCookFood(BaseApp.canteenId, cookMode) + } + + suspend fun deleteCookFood(foodId: String, cookMode: Int, dinnerType: String) = withContext(Dispatchers.IO) { + db.cookFoodDao().deleteCookFood(BaseApp.canteenId, foodId, cookMode, dinnerType) + } + + /** 接口提交成功后逻辑删除菜品,同时把 submitState 置 1 */ + suspend fun markSubmittedAndDelete(foodId: String, cookMode: Int, dinnerType: String) = withContext(Dispatchers.IO) { + db.cookFoodDao().markSubmittedAndDelete(BaseApp.canteenId, foodId, cookMode, dinnerType) + } + + suspend fun deleteCookFoodGoodsList(pid: Long, foodId: String) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().deleteCookFoodGoodsList(pid, foodId) + } + + /** 仅逻辑删除调料行(materialType = 3),主辅材不动 */ + suspend fun deleteSeasoningOnly(pid: Long, foodId: String) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().deleteSeasoningOnly(pid, foodId) + } + //-------------------------------------------------------------------------------------------- + + suspend fun insertGoodsList(items: MutableList) = + withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().insertGoodsList(items) + } + + suspend fun getCookFoodGoodsList(pid: Long, foodId: String) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().getCookFoodGoodsList(pid, foodId) + } + + suspend fun getCookFoodGoodsListByType( + foodId: String, + materialType: Int + ) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().getCookFoodGoodsListByType(foodId, materialType) + } + //-------------------------------------------------------------------------------------------- + + suspend fun getSeasoningById(id: Long) = withContext(Dispatchers.IO) { + db.seasoningDao().getSeasoningById(id) + } + + suspend fun getSeasoningBySort(sort: Int) = withContext(Dispatchers.IO) { + db.seasoningDao().getSeasoningBySort(sort) + } + + suspend fun getSeasoningByGoodsId(goodsId: String) = withContext(Dispatchers.IO) { + db.seasoningDao().getSeasoningByGoodsId(goodsId) + } + + suspend fun getAllStream() = withContext(Dispatchers.IO) { + db.seasoningDao().getAllStream() + } + + suspend fun search(query: String) = withContext(Dispatchers.IO) { + db.seasoningDao().search(query) + } + + suspend fun clearAllSeasoning() = withContext(Dispatchers.IO) { + db.seasoningDao().clearAllSeasoning() + } + + suspend fun deleteSeasoningBySort(sort: Int) = withContext(Dispatchers.IO) { + db.seasoningDao().deleteSeasoningBySort(sort) + } + + suspend fun insertSeasoningList(items: MutableList) = + withContext(Dispatchers.IO) { + db.seasoningDao().insertSeasoningList(items) + } + + suspend fun updateSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { + db.seasoningDao().updateSeasoning(item) + } + + suspend fun insertSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { + db.seasoningDao().insertSeasoning(item) + } + + /** + * 按 goodsId 查重后 upsert:存在则更新,不存在则插入 + * 确保同一个 goodsId 在 dm_seasoning 中只有一条记录 + */ + suspend fun upsertSeasoningByGoodsId(item: SeasoningEntity) = withContext(Dispatchers.IO) { + val existing = db.seasoningDao().getSeasoningByGoodsId(item.goodsId) + if (existing != null) { + db.seasoningDao().updateSeasoning(item.also { it.id = existing.id }) + } else { + item.id = 0 + db.seasoningDao().insertSeasoning(item) + } + } + + /** + * 查询指定设备是否已配置调料槽位 + * @param deviceId 目标设备 ID + * @return true 表示已有配置,false 表示未配置 + */ + suspend fun hasSeasoningSlotConfig(deviceId: String) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().queryByDeviceId(deviceId).isNotEmpty() + } + + /** + * 查询所有设备的调料槽位配置 + */ + suspend fun getAllSeasoningSlots() = withContext(Dispatchers.IO) { + db.seasoningSlotDao().queryAll() + } + + /** + * 查询指定设备的调料槽位配置 + * @param deviceId 目标设备 ID + */ + suspend fun getSeasoningSlotsByDeviceId(deviceId: String) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().queryByDeviceId(deviceId) + } + + /** + * 插入或更新单个调料槽位配置 + */ + suspend fun upsertSeasoningSlot(slot: SeasoningSlotEntity) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().upsert(slot) + } + + /** + * 批量插入或更新调料槽位配置 + */ + suspend fun upsertAllSeasoningSlots(slots: List) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().upsertAll(slots) + } + + /** + * 删除指定设备的指定槽位配置 + * @param deviceId 目标设备 ID + * @param address 秤硬件地址 + */ + suspend fun deleteSeasoningSlot(deviceId: String, address: Int) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().deleteByDeviceIdAndAddress(deviceId, address) + } + + /** + * 删除指定设备的全部槽位配置 + * @param deviceId 目标设备 ID + */ + suspend fun deleteAllSlotsByDeviceId(deviceId: String) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().deleteAllByDeviceId(deviceId) + } + + // ---- 调试用:查询各表全量数据(含已删除记录)---- + + /** 查询 dm_cook_food 全部记录 */ + suspend fun getAllCookFood() = withContext(Dispatchers.IO) { + db.cookFoodDao().getAllCookFood() + } + + /** 查询 dm_cook_food_goods 全部记录 */ + suspend fun getAllCookFoodGoods() = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().getAllCookFoodGoods() + } + + /** 查询 dm_seasoning 全部记录 */ + suspend fun getAllSeasoning() = withContext(Dispatchers.IO) { + db.seasoningDao().getAllSeasoning() + } + + // ---- 调试用:分页查询(含 isDel 过滤)---- + + suspend fun getCookFoodPaged(showDel: Boolean, limit: Int, offset: Int) = + withContext(Dispatchers.IO) { + db.cookFoodDao().getCookFoodPaged(if (showDel) 1 else 0, limit, offset) + } + + suspend fun countCookFoodAll(showDel: Boolean) = withContext(Dispatchers.IO) { + db.cookFoodDao().countCookFoodAll(if (showDel) 1 else 0) + } + + suspend fun getCookFoodGoodsPaged(showDel: Boolean, limit: Int, offset: Int) = + withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().getCookFoodGoodsPaged(if (showDel) 1 else 0, limit, offset) + } + + suspend fun countCookFoodGoodsAll(showDel: Boolean) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().countCookFoodGoodsAll(if (showDel) 1 else 0) + } + + suspend fun getSeasoningPaged(showDel: Boolean, limit: Int, offset: Int) = + withContext(Dispatchers.IO) { + db.seasoningDao().getSeasoningPaged(if (showDel) 1 else 0, limit, offset) + } + + suspend fun countSeasoningAll(showDel: Boolean) = withContext(Dispatchers.IO) { + db.seasoningDao().countSeasoningAll(if (showDel) 1 else 0) + } + + suspend fun getSlotPaged(limit: Int, offset: Int) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().getSlotPaged(limit, offset) + } + + suspend fun countSlotAll() = withContext(Dispatchers.IO) { + db.seasoningSlotDao().countSlotAll() + } + + // ---- 调试用:动态条件过滤分页查询 ---- + + suspend fun getCookFoodPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.cookFoodDao().queryPagedFiltered(query) + } + + suspend fun countCookFoodFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.cookFoodDao().countFiltered(query) + } + + suspend fun getCookFoodGoodsPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().queryPagedFiltered(query) + } + + suspend fun countCookFoodGoodsFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().countFiltered(query) + } + + suspend fun getSeasoningPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.seasoningDao().queryPagedFiltered(query) + } + + suspend fun countSeasoningFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.seasoningDao().countFiltered(query) + } + + suspend fun getSlotPagedFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().queryPagedFiltered(query) + } + + suspend fun countSlotFiltered(query: SupportSQLiteQuery) = withContext(Dispatchers.IO) { + db.seasoningSlotDao().countFiltered(query) + } + + /** + * 物理清空全部 4 张表的测试数据 + * 清除顺序:先删子表(食材),再删主表(菜品),最后删配置表(调料、槽位) + */ + suspend fun clearAllData() = withContext(Dispatchers.IO) { + db.cookFoodGoodsDao().deleteAll() + db.cookFoodDao().deleteAll() + db.seasoningDao().deleteAll() + db.seasoningSlotDao().deleteAll() + } +} +// +//class SeasoningRepository(val seasoningDao: SeasoningDao) { +// val seasoningList: Flow> = seasoningDao.getAllStream() +// +// fun addSeasoning(item: SeasoningEntity) = withContext(Dispatchers.IO) { +// seasoningDao.insert(item) +// } +// +// fun searchSeasoning(query: String) = withContext(Dispatchers.IO) { +// seasoningDao.search(query) +// } +//} +// +//class CookFoodRepository(val cookFoodDao: CookFoodDao) { +// fun getCookFood(foodId: String, cookMode:Int = 0) = withContext(Dispatchers.IO) { +// cookFoodDao.getCookFoodById(foodId, cookMode) +// } +// +// fun getCookFoodByList(cookMode:Int = 0) = withContext(Dispatchers.IO) { +// cookFoodDao.getCookFoodList(cookMode) +// } +// +// fun countCookFood(cookMode:Int = 0)= withContext(Dispatchers.IO) { +// cookFoodDao.countCookFood(cookMode) +// } +//} +// +//class CookFoodGoodsRepository(val cookFoodGoodsDao: CookFoodGoodsDao) { +// fun getFoodGoodsList(foodId: String) = withContext(Dispatchers.IO) { +// cookFoodGoodsDao.getCookFoodGoodsList(foodId) +// } +// +//} + diff --git a/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt b/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt new file mode 100644 index 0000000..99ca786 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/DbViewModel.kt @@ -0,0 +1,623 @@ +package com.shuwei.dish.match.db + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import androidx.sqlite.db.SimpleSQLiteQuery +import com.shuwei.dish.match.model.CollectedFoodItem +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.db.entity.SeasoningEntity +import com.shuwei.dish.match.db.entity.SeasoningSlotEntity +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock + +class DbViewModel : ViewModel() { + + private val rep = DbRepository() + + fun updateCookFood(entity: CookFoodEntity) { + viewModelScope.launch { + rep.updateCookFood(entity) + } + } + + private val _cookFoodDetailState = MutableStateFlow(null) + val cookFoodDetailState: StateFlow = _cookFoodDetailState.asStateFlow() + + fun getCookFoodById(foodId: String, cookMode: Int, dinnerType: String) { + viewModelScope.launch { + _cookFoodDetailState.value = rep.getCookFoodById(foodId, cookMode, dinnerType) + } + } + + private val _cookFoodListState = MutableStateFlow?>(null) + val cookFoodListState: StateFlow?> = _cookFoodListState.asStateFlow() + fun resetCookFoodList() { + _cookFoodListState.value = null + } + fun getCookFoodList( + cookMode: Int, + dinnerType: String = "0" + ) { + viewModelScope.launch { + _cookFoodListState.value = rep.getCookFoodList(cookMode, dinnerType) + } + } + + /** + * 查询本地烹饪中数据(回调版本),结果通过 onResult 直接回调,不经过 StateFlow + * 适合不需要跨生命周期持续监听的一次性查询场景 + */ + fun getCookFoodListWithCallback( + cookMode: Int, + dinnerType: String = "0", + onResult: (MutableList?) -> Unit + ) { + viewModelScope.launch { + onResult(rep.getCookFoodList(cookMode, dinnerType)) + } + } + + /** + * 直接返回本地烹饪中数据,供需要串行等待结果的场景使用(如先查库再发网络请求) + */ + suspend fun getCookFoodListDirect( + cookMode: Int, + dinnerType: String = "0" + ): MutableList? { + return rep.getCookFoodList(cookMode, dinnerType) + } + + suspend fun countCookFood(cookMode: Int): Int { + return rep.countCookFood(cookMode) + } + +// fun saveCookFood(cookMode: Int, entity: CookFoodEntity) { +// viewModelScope.launch { +// val data = rep.getCookFoodById(entity.foodId, cookMode) +// if (data == null) { +// rep.insertCookFood(entity) +// return@launch +// } +// rep.updateCookFood(entity) +// } +// } + + suspend fun saveCookFoodAndGoods( + cookMode: Int, + entity: CookFoodEntity, + list: MutableList? + ) { + val data = rep.getCookFoodById(entity.foodId, cookMode, entity.dinnerType ?: "0") + if (data == null) { + // 首次保存:主表 + 主辅材 + 调料 全量 insert + rep.insertCookFood(entity) + val newData = rep.getCookFoodById(entity.foodId, cookMode, entity.dinnerType ?: "0") + // 回填 id,防止下次调用时 getCookFoodById 查不到而重复插入主辅材 + newData?.let { entity.id = it.id } + list?.let { + it.forEach { goods -> + goods.id = 0 + goods.pid = newData?.id ?: 0 + } + rep.insertGoodsList(it) + } + } else { + // 已存在:主表与主辅材保持不变,仅重建调料部分,避免主表/主辅材累积逻辑删除脏数据 + rep.deleteSeasoningOnly(data.id, entity.foodId) + val seasoningList = list?.filter { it.materialType == 3 }?.toMutableList() + if (!seasoningList.isNullOrEmpty()) { + seasoningList.forEach { goods -> + goods.id = 0 + goods.pid = data.id + goods.foodId = entity.foodId + } + rep.insertGoodsList(seasoningList) + } + } + } + + suspend fun deleteCookFoodAndGoods(cookMode: Int, foodId: String, dinnerType: String) { + val data = rep.getCookFoodById(foodId, cookMode, dinnerType) + if (data != null) { + rep.deleteCookFood(foodId = foodId, cookMode = cookMode, dinnerType = dinnerType) + rep.deleteCookFoodGoodsList(pid = data.id, foodId = foodId) + } + } + + /** + * 接口提交成功后删除本地菜品草稿(异步版本,绑定 viewModelScope) + * 与 [markSubmittedAndDelete] 的区别:不是 suspend,内部用 viewModelScope 启动协程, + * 不受调用方 Activity 生命周期影响,适合 singleTask 跳转前触发删除的场景 + */ + fun markSubmittedAndDeleteAsync(cookMode: Int, foodId: String, dinnerType: String) { + viewModelScope.launch { + markSubmittedAndDelete(cookMode, foodId, dinnerType) + } + } + + /** + * 接口提交成功后删除本地菜品草稿 + * 与 [deleteCookFoodAndGoods] 的区别:主表 submitState 会被置 1,便于审计区分手动删除与接口删除 + */ + suspend fun markSubmittedAndDelete(cookMode: Int, foodId: String, dinnerType: String) { + val data = rep.getCookFoodById(foodId, cookMode, dinnerType) + if (data != null) { + rep.markSubmittedAndDelete(foodId = foodId, cookMode = cookMode, dinnerType = dinnerType) + rep.deleteCookFoodGoodsList(pid = data.id, foodId = foodId) + } + } + + //-------------------------------------------------------------------------------------------------------- + + suspend fun getCookFoodGoodsList( + foodId: String, + cookMode: Int, + dinnerType: String + ): MutableList { + val cookFood = rep.getCookFoodById(foodId, cookMode, dinnerType) + return if (cookFood != null) rep.getCookFoodGoodsList(pid = cookFood.id, foodId = cookFood.foodId) + else mutableListOf() + } + +// fun updateGoods(entity: CookFoodGoodsEntity) { +// viewModelScope.launch { +// rep.updateGoods(entity) +// } +// } + +// fun saveCookFoodGoods(list: MutableList) { +// viewModelScope.launch { +// val foodId = list[0].foodId +// val data: MutableList? = +// rep.getCookFoodGoodsList(foodId!!) +// if (data.isNullOrEmpty()) { +// rep.insertGoodsList(list) +// return@launch +// } +// val localDbSeasoningList = rep.getCookFoodGoodsListByType(foodId, 3) +// localDbSeasoningList.forEach { +// it.isDel = 1 +// //更新本地调料数据为已删除的状态 +// rep.updateGoods(it) +// } +// //筛选出调料数据保存,主辅材不会变更不需要再次处理 +// val saveSeasoningList = list.filter { it.materialType == 3 } +// rep.insertGoodsList(saveSeasoningList.toMutableList()) +// } +// } + + //-------------------------------------------------------------------------------------------------------- + +// fun addSeasoning(list: MutableList, callback: () -> Unit) { +// viewModelScope.launch { +// rep.deleteBatch() +// rep.insertSeasoningList(items = list) +// callback() +// } +// } + + private val loadSeasoningMutex = Mutex() + + /** + * 本地调料列表状态流 + */ + private val _seasoningState = MutableStateFlow>(mutableListOf()) + val seasoningState: StateFlow> = _seasoningState.asStateFlow() + + fun loadSeasoning() { + if (loadSeasoningMutex.isLocked) return + viewModelScope.launch { + loadSeasoningMutex.withLock { + _seasoningState.value = rep.getAllStream() + } + } + } + + /** + * 所有设备调料槽位配置的 UI 状态流 + */ + private val _seasoningSlotState = MutableStateFlow>(emptyList()) + val seasoningSlotState: StateFlow> = _seasoningSlotState.asStateFlow() + + /** + * 查询所有调料槽位配置,更新 seasoningSlotState 并返回结果 + * 声明为 suspend,调用方可在同一协程中直接使用返回值,无需等待 StateFlow 更新 + */ + suspend fun loadSeasoningSlot(): List { + val slots = rep.getAllSeasoningSlots() + _seasoningSlotState.value = slots + return slots + } + + fun deleteSeasoningBySort(sort: Int) { + viewModelScope.launch { + rep.deleteSeasoningBySort(sort) + } + } + + private val _clearAllSeasoningState = MutableStateFlow(false) + val clearAllSeasoningState: StateFlow = _clearAllSeasoningState.asStateFlow() + + fun clearAllSeasoning() { + viewModelScope.launch { + rep.clearAllSeasoning() + _clearAllSeasoningState.value = true + } + } + + private val _hasSeasoningSlotConfigState = MutableStateFlow(false) + val hasSeasoningSlotConfigState: StateFlow = _hasSeasoningSlotConfigState.asStateFlow() + + /** + * 查询指定设备是否已配置调料槽位 + * @param deviceId 目标设备 ID + */ + fun hasSeasoningSlotConfig(deviceId: String) { + viewModelScope.launch { + _hasSeasoningSlotConfigState.value = rep.hasSeasoningSlotConfig(deviceId) + } + } + + /** + * 查询指定设备的调料槽位配置 + */ + suspend fun getSeasoningSlotsByDeviceId(deviceId: String) = + rep.getSeasoningSlotsByDeviceId(deviceId) + + /** + * 插入或更新单个调料槽位配置 + */ + suspend fun upsertSeasoningSlot(slot: SeasoningSlotEntity) = + rep.upsertSeasoningSlot(slot) + + /** + * 批量插入或更新调料槽位配置 + */ + suspend fun upsertAllSeasoningSlots(slots: List) = + rep.upsertAllSeasoningSlots(slots) + + /** + * 删除指定设备的指定槽位配置 + * @param deviceId 目标设备 ID + * @param address 秤硬件地址 + */ + suspend fun deleteSeasoningSlot(deviceId: String, address: Int) = + rep.deleteSeasoningSlot(deviceId, address) + + /** + * 删除指定设备的全部槽位配置(子设备接收全量广播时先清空再写入) + * @param deviceId 目标设备 ID + */ + suspend fun deleteAllSlotsByDeviceId(deviceId: String) = + rep.deleteAllSlotsByDeviceId(deviceId) + + // ---- 数据库调试查看 ---- + + /** + * 物理清空全部 4 张表的测试数据,完成后通过回调通知 UI + * @param onDone 清除完成后在主线程执行的回调 + */ + fun clearAllData(onDone: () -> Unit) { + viewModelScope.launch { + rep.clearAllData() + onDone() + } + } + + companion object { + /** 每页加载条数 */ + const val DB_INSPECT_PAGE_SIZE = 50 + } + + /** + * 单条数据库记录的展示模型 + * @param isDel 是否已逻辑删除 + * @param fields 字段名-值对列表,按顺序排列 + * @param columnsPerRow 每行显示的列数,默认 3;槽位表传 1 保持单列 + */ + data class DbRecord( + val isDel: Boolean, + val fields: List>, + val columnsPerRow: Int = 3 + ) + + /** + * 数据库调试页面 UI 状态 + * @param items 当前已加载的记录列表 + * @param totalCount 当前过滤条件下的总记录数 + * @param hasMore 是否还有更多数据可加载 + * @param isLoading 是否正在加载(首次加载或加载更多) + * @param filter 当前生效的筛选条件,供"加载更多"复用 + */ + data class DbInspectUiState( + val items: List = emptyList(), + val totalCount: Int = 0, + val hasMore: Boolean = false, + val isLoading: Boolean = false, + val filter: DbInspectFilter = DbInspectFilter() + ) + + /** + * 数据库调试页面筛选条件 + * @param showDel 是否包含已删除记录 + * @param exactFields 精确匹配字段:列名 → 值 + * @param likeFields 模糊匹配字段:列名 → 值(自动加 %value%) + */ + data class DbInspectFilter( + val showDel: Boolean = true, + val exactFields: Map = emptyMap(), + val likeFields: Map = emptyMap() + ) + + private val _dbInspectState = MutableStateFlow(DbInspectUiState()) + val dbInspectState: StateFlow = _dbInspectState.asStateFlow() + + /** + * 加载指定表的第一页数据(切换 Tab / 修改筛选条件时调用) + * @param tableIndex 0=菜品 1=食材 2=调料 3=槽位 + * @param filter 筛选条件,包含 showDel 和各字段过滤值 + */ + fun loadDbInspect(tableIndex: Int, filter: DbInspectFilter) { + viewModelScope.launch { + // 立即清空旧数据并显示加载状态,避免切换 Tab 时旧数据闪现 + _dbInspectState.value = DbInspectUiState(isLoading = true, filter = filter) + val (items, total) = queryPage(tableIndex, filter, offset = 0) + _dbInspectState.value = DbInspectUiState( + items = items, + totalCount = total, + hasMore = items.size < total, + isLoading = false, + filter = filter + ) + } + } + + /** + * 追加加载下一页数据(点击"加载更多"时调用) + * 筛选条件从当前 state 中复用,无需重新传入 + * @param tableIndex 0=菜品 1=食材 2=调料 3=槽位 + */ + fun loadMoreDbInspect(tableIndex: Int) { + viewModelScope.launch { + val current = _dbInspectState.value + // 已在加载中或无更多数据时直接返回,防止重复请求 + if (!current.hasMore || current.isLoading) return@launch + _dbInspectState.value = current.copy(isLoading = true) + val (newItems, total) = queryPage(tableIndex, current.filter, offset = current.items.size) + val merged = current.items + newItems + _dbInspectState.value = DbInspectUiState( + items = merged, + totalCount = total, + hasMore = merged.size < total, + isLoading = false, + filter = current.filter + ) + } + } + + /** + * 执行分页查询,返回 DbRecord 列表和总记录数 + * 通过 buildQueries 将 filter 转换为动态 SQL + */ + private suspend fun queryPage( + tableIndex: Int, + filter: DbInspectFilter, + offset: Int + ): Pair, Int> { + val limit = DB_INSPECT_PAGE_SIZE + return when (tableIndex) { + 0 -> { + val (pageQ, countQ) = buildQueries("dm_cook_food", "createTime DESC", true, filter, limit, offset) + val total = rep.countCookFoodFiltered(countQ) + val rows = rep.getCookFoodPagedFiltered(pageQ).map { e -> + DbRecord( + isDel = e.isDel == 1, + fields = listOf( + "id" to "${e.id}", "foodId" to "${e.foodId}", "foodName" to "${e.foodName}", + "canteenId" to "${e.canteenId}", "foodWeight" to "${e.foodWeight}", "cookMode" to "${e.cookMode}", + "dinnerType" to "${e.dinnerType}", "isDel" to "${e.isDel}", "submitState" to "${e.submitState}", + "createTime" to "${e.createTime}" + ) + ) + } + rows to total + } + 1 -> { + val (pageQ, countQ) = buildQueries("dm_cook_food_goods", "createTime DESC", true, filter, limit, offset) + val total = rep.countCookFoodGoodsFiltered(countQ) + val rows = rep.getCookFoodGoodsPagedFiltered(pageQ).map { e -> + DbRecord( + isDel = e.isDel == 1, + fields = listOf( + "id" to "${e.id}", "pid" to "${e.pid}", "goodsId" to "${e.goodsId}", + "goodsName" to "${e.goodsName}", "foodId" to "${e.foodId}", "materialType" to "${e.materialType}", + "relateionType" to "${e.relateionType}", "allEdible" to "${e.allEdible}", "useWeight" to "${e.useWeight}", + "sort" to "${e.sort}", "zjmCode" to "${e.zjmCode}", "materId" to "${e.materId}", + "isDel" to "${e.isDel}", "createTime" to "${e.createTime}" + ) + ) + } + rows to total + } + 2 -> { + val (pageQ, countQ) = buildQueries("dm_seasoning", "sort ASC", true, filter, limit, offset) + val total = rep.countSeasoningFiltered(countQ) + val rows = rep.getSeasoningPagedFiltered(pageQ).map { e -> + DbRecord( + isDel = e.isDel == 1, + fields = listOf( + "id" to "${e.id}", "goodsId" to "${e.goodsId}", "goodsName" to "${e.goodsName}", + "sort" to "${e.sort}", "materialType" to "${e.materialType}", "useWeight" to "${e.useWeight}", + "zjmCode" to "${e.zjmCode}", "materId" to "${e.materId}", "isDel" to "${e.isDel}", + "createTime" to "${e.createTime}" + ) + ) + } + rows to total + } + 3 -> { + val (pageQ, countQ) = buildQueries("dm_seasoning_slot", "deviceId ASC, address ASC", false, filter, limit, offset) + val total = rep.countSlotFiltered(countQ) + val rows = rep.getSlotPagedFiltered(pageQ).map { e -> + DbRecord( + isDel = false, + fields = listOf( + "deviceId" to e.deviceId, "address" to "${e.address}", + "goodsId" to e.goodsId, "goodsName" to e.goodsName + ), + columnsPerRow = 1 + ) + } + rows to total + } + else -> emptyList() to 0 + } + } + + /** + * 根据筛选条件动态构建分页查询和计数查询的 SQL + * @param tableName 表名 + * @param orderBy ORDER BY 子句(不含关键字) + * @param hasIsDel 是否有 isDel 字段,无则忽略 showDel 参数 + * @param filter 筛选条件 + * @param limit 分页大小 + * @param offset 偏移量 + * @return Pair<分页查询, 计数查询> + */ + private fun buildQueries( + tableName: String, + orderBy: String, + hasIsDel: Boolean, + filter: DbInspectFilter, + limit: Int, + offset: Int + ): Pair { + val conditions = mutableListOf() + val args = mutableListOf() + + if (hasIsDel && !filter.showDel) { + conditions.add("isDel = 0") + } + filter.exactFields.forEach { (col, value) -> + if (value.isNotBlank()) { + conditions.add("$col = ?") + args.add(value) + } + } + filter.likeFields.forEach { (col, value) -> + if (value.isNotBlank()) { + conditions.add("$col LIKE ?") + args.add("%$value%") + } + } + + val where = if (conditions.isEmpty()) "" else "WHERE ${conditions.joinToString(" AND ")}" + val pageQuery = SimpleSQLiteQuery( + "SELECT * FROM $tableName $where ORDER BY $orderBy LIMIT ? OFFSET ?", + (args + limit + offset).toTypedArray() + ) + val countQuery = SimpleSQLiteQuery( + "SELECT COUNT(*) FROM $tableName $where", + args.toTypedArray() + ) + return pageQuery to countQuery + } + + //-------------------------------------------------------------------------------------------------------- + +// viewModelScope.launch { +// list.forEach { +// rep.updateSeasoning(it) +// } +// } +// } + + fun saveSeasoning(entity: SeasoningEntity, block: () -> Unit) { + viewModelScope.launch { + val list = rep.getSeasoningBySort(entity.sort) + val size = list?.size?:0 + if (size == 1) { + rep.updateSeasoning(entity.also { it.id = list!![0].id }) + block() + return@launch + } + if (size > 1) { + list?.forEach { + rep.deleteSeasoningBySort(it.sort) + } + } + entity.id = 0 + val id = rep.insertSeasoning(entity) + entity.id = id + block() + } + } + + /** + * 按 goodsId 查重后 upsert 调料数据,确保同一 goodsId 只有一条记录 + */ + suspend fun upsertSeasoningByGoodsId(entity: SeasoningEntity) { + rep.upsertSeasoningByGoodsId(entity) + } + + /** + * 按 goodsId 查询调料详情 + */ + suspend fun getSeasoningByGoodsId(goodsId: String): SeasoningEntity? { + return rep.getSeasoningByGoodsId(goodsId) + } + + //-------------------------------------------------------------------------------------------------------- + + /** + * 已采集食材列表 UI 状态流 + * 元素为按名称分组后的(名称,数量)数据 + */ + private val _collectedFoodListState = MutableStateFlow>(emptyList()) + val collectedFoodListState: StateFlow> = _collectedFoodListState.asStateFlow() + +// /** +// * 加载已采集食材列表,按名称分组统计数量 +// * 使用属性查询只读 name 字段,不加载 foodVector,节省内存 +// * @param nameFilter 名称过滤关键词,为空时加载全部 +// */ +// fun loadCollectedFoodList(nameFilter: String? = null) { +// viewModelScope.launch { +// val result = ObjectBox.queryFoodNameCounts(nameFilter) +// _collectedFoodListState.value = result.map { (name, count) -> +// CollectedFoodItem(foodName = name, collectCount = count) +// } +// } +// } + +// /** +// * 删除指定名称的所有采集记录,删除完成后刷新列表 +// * @param foodName 食材名称 +// * @param nameFilter 当前搜索关键词,用于删除后刷新列表 +// */ +// fun removeCollectedFood(foodName: String, nameFilter: String? = null) { +// viewModelScope.launch { +// ObjectBox.remove(foodName) +// loadCollectedFoodList(nameFilter) +// } +// } + +// /** +// * 清除所有向量采集数据,完成后刷新列表 +// * @param onDone 清除完成后在主线程执行的回调 +// */ +// fun clearAllCollectedFood(onDone: (() -> Unit)? = null) { +// viewModelScope.launch { +// ObjectBox.removeAll() +// loadCollectedFoodList() +// onDone?.invoke() +// } +// } + + //-------------------------------------------------------------------------------------------------------- +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt deleted file mode 100644 index 19cf5e1..0000000 --- a/app/src/main/java/com/shuwei/dish/match/db/dao/AppDao.kt +++ /dev/null @@ -1,92 +0,0 @@ -package com.shuwei.dish.match.db.dao - -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Update -import com.shuwei.dish.match.entity.CookFoodEntity -import com.shuwei.dish.match.entity.CookFoodGoodsEntity -import com.shuwei.dish.match.entity.SeasoningEntity -import kotlinx.coroutines.flow.Flow - -@Dao -interface AppDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertCookFood(item: CookFoodEntity): Long - - @Update - suspend fun updateCookFood(item: CookFoodEntity) - - @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode") - suspend fun getCookFoodById(canteenId:String, foodId: String, cookMode: Int): CookFoodEntity? - - @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND cookMode = :cookMode AND dinnerType = :dinnerType ORDER BY createTime DESC") - suspend fun getCookFoodList(canteenId:String, cookMode: Int, dinnerType:String): MutableList? - - @Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND cookMode = :cookMode") - suspend fun countCookFood(canteenId:String, cookMode: Int): Int - - @Query("DELETE FROM dm_cook_food WHERE canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode") - suspend fun deleteCookFood(canteenId:String, foodId: String, cookMode: Int) - - //-------------------------------------------------------------------------------------------- - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertGoods(item: CookFoodGoodsEntity): Long - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertGoodsList(items: MutableList): Array - - @Update - suspend fun updateGoods(item: CookFoodGoodsEntity) - - @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND pid = :pid AND foodId = :foodId") - suspend fun getCookFoodGoodsList(pid:Long, foodId: String): MutableList - - @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType") - suspend fun getCookFoodGoodsListByType( - foodId: String, - materialType: Int - ): MutableList - - @Query("DELETE FROM dm_cook_food_goods WHERE pid = :pid AND foodId = :foodId") - suspend fun deleteCookFoodGoodsList(pid:Long, foodId: String) - - //-------------------------------------------------------------------------------------------- - - @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND id = :id") - suspend fun getSeasoningById(id: Long): SeasoningEntity? - - @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND sort = :sort") - suspend fun getSeasoningBySort(sort: Int): MutableList? - - @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId = :goodsId") - suspend fun getSeasoningByGoodsId(goodsId: Int): SeasoningEntity? - -// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId != -1 AND goodsName != '' ORDER BY sort ASC") -// fun getAllStream(): Flow> - - @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId != -1 AND goodsName != '' ORDER BY sort ASC") - fun getAllStream(): MutableList - - @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsName LIKE '%' || :query || '%'") - suspend fun search(query: String): MutableList - - @Query("DELETE FROM dm_seasoning WHERE isDel = 0 AND sort = :sort") - suspend fun deleteSeasoningBySort(sort:Int): Int - - @Query("DELETE FROM dm_seasoning WHERE isDel = 0") - suspend fun clearAllSeasoning(): Int - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertSeasoningList(items: MutableList): Array - - @Update - suspend fun updateSeasoning(item: SeasoningEntity) - - @Insert - suspend fun insertSeasoning(item: SeasoningEntity): Long - //-------------------------------------------------------------------------------------------- -} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt index f09e59f..10a8849 100644 --- a/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodDao.kt @@ -1,20 +1,61 @@ -//package com.shuwei.dish.match.db.dao -// -//import androidx.room.Dao -//import androidx.room.Query -//import com.shuwei.dish.match.db.BaseDao -//import com.shuwei.dish.match.entity.CookFoodEntity -// -//@Dao -//interface CookFoodDao : BaseDao { -// -// @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND foodId = :foodId AND cookMode = :cookMode") -// suspend fun getCookFoodById(foodId: String, cookMode:Int): CookFoodEntity? -// -// @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND cookMode = :cookMode") -// suspend fun getCookFoodList(cookMode:Int): MutableList? -// -// @Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND cookMode = :cookMode") -// suspend fun countCookFood(cookMode:Int): Int -// -//} \ No newline at end of file +package com.shuwei.dish.match.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.RawQuery +import androidx.room.Update +import androidx.sqlite.db.SupportSQLiteQuery +import com.shuwei.dish.match.db.entity.CookFoodEntity + +@Dao +interface CookFoodDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertCookFood(item: CookFoodEntity): Long + + @Update + suspend fun updateCookFood(item: CookFoodEntity) + + @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode AND dinnerType = :dinnerType") + suspend fun getCookFoodById(canteenId: String, foodId: String, cookMode: Int, dinnerType: String): CookFoodEntity? + + @Query("SELECT * FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND cookMode = :cookMode AND dinnerType = :dinnerType ORDER BY createTime DESC") + suspend fun getCookFoodList(canteenId: String, cookMode: Int, dinnerType: String): MutableList? + + @Query("SELECT count(1) FROM dm_cook_food WHERE isDel = 0 AND canteenId = :canteenId AND cookMode = :cookMode") + suspend fun countCookFood(canteenId: String, cookMode: Int): Int + + // @Query("DELETE FROM dm_cook_food WHERE canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode") + @Query("UPDATE dm_cook_food SET isDel = 1 WHERE isDel = 0 AND canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode AND dinnerType = :dinnerType") + suspend fun deleteCookFood(canteenId: String, foodId: String, cookMode: Int, dinnerType: String) + + /** 接口提交成功后逻辑删除菜品,同时把 submitState 置 1 以便在数据库审计中区分手动删除 */ + @Query("UPDATE dm_cook_food SET isDel = 1, submitState = 1 WHERE isDel = 0 AND canteenId = :canteenId AND foodId = :foodId AND cookMode = :cookMode AND dinnerType = :dinnerType") + suspend fun markSubmittedAndDelete(canteenId: String, foodId: String, cookMode: Int, dinnerType: String) + + /** 查询全部菜品记录(含已删除),用于数据库调试查看 */ + @Query("SELECT * FROM dm_cook_food ORDER BY createTime DESC") + suspend fun getAllCookFood(): MutableList + + /** 分页查询菜品记录,showDel=true 时包含已删除记录 */ + @Query("SELECT * FROM dm_cook_food WHERE (:showDel = 1 OR isDel = 0) ORDER BY createTime DESC LIMIT :limit OFFSET :offset") + suspend fun getCookFoodPaged(showDel: Int, limit: Int, offset: Int): List + + /** 统计菜品记录总数,showDel=true 时包含已删除记录 */ + @Query("SELECT COUNT(*) FROM dm_cook_food WHERE (:showDel = 1 OR isDel = 0)") + suspend fun countCookFoodAll(showDel: Int): Int + + /** 物理清空全表,用于清除测试数据 */ + @Query("DELETE FROM dm_cook_food") + suspend fun deleteAll() + + /** 动态条件分页查询菜品记录,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List + + /** 动态条件统计菜品记录总数,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun countFiltered(query: SupportSQLiteQuery): Int +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt index cd0b298..c5ec518 100644 --- a/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/CookFoodGoodsDao.kt @@ -1,29 +1,61 @@ -//package com.shuwei.dish.match.db.dao -// -//import androidx.room.Dao -//import androidx.room.Query -//import com.shuwei.dish.match.db.BaseDao -//import com.shuwei.dish.match.entity.CookFoodGoodsEntity -// -//@Dao -//interface CookFoodGoodsDao : BaseDao { -// -// @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId") -// suspend fun getCookFoodGoodsList(foodId: String): MutableList -// -// @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType") -// suspend fun getCookFoodGoodsListByType(foodId: String, materialType:Int): MutableList -// -//// @Query( -//// """ -//// UPDATE dm_cook_food_goods -//// SET goodsName=:entity.goodsName, foodId=:entity.foodId, relateionType=:entity.relateionType, sort=:entity.sort -//// allEdible=:entity.allEdible, useWeight=:entity.useWeight, isDel=:entity.isDel, createTime=:entity.createTime -//// WHERE isDel = 0 -//// AND foodId=:entity.foodId -//// AND goodsId=:goodsId -//// """ -//// ) -//// fun updateByGoodsId(entity: CookFoodGoodsEntity) -// -//} \ No newline at end of file +package com.shuwei.dish.match.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.RawQuery +import androidx.room.Update +import androidx.sqlite.db.SupportSQLiteQuery +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity + +@Dao +interface CookFoodGoodsDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertGoods(item: CookFoodGoodsEntity): Long + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertGoodsList(items: MutableList): Array + + @Update + suspend fun updateGoods(item: CookFoodGoodsEntity) + + @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND pid = :pid AND foodId = :foodId") + suspend fun getCookFoodGoodsList(pid: Long, foodId: String): MutableList + + @Query("SELECT * FROM dm_cook_food_goods WHERE isDel = 0 AND foodId = :foodId AND materialType = :materialType") + suspend fun getCookFoodGoodsListByType(foodId: String, materialType: Int): MutableList + + // @Query("DELETE FROM dm_cook_food_goods WHERE pid = :pid AND foodId = :foodId") + @Query("UPDATE dm_cook_food_goods SET isDel = 1 WHERE isDel = 0 AND pid = :pid AND foodId = :foodId") + suspend fun deleteCookFoodGoodsList(pid: Long, foodId: String) + + /** 仅逻辑删除调料行(materialType = 3),主辅材不动,用于 cook 时只重建调料部分 */ + @Query("UPDATE dm_cook_food_goods SET isDel = 1 WHERE isDel = 0 AND pid = :pid AND foodId = :foodId AND materialType = 3") + suspend fun deleteSeasoningOnly(pid: Long, foodId: String) + + /** 查询全部食材记录(含已删除),用于数据库调试查看 */ + @Query("SELECT * FROM dm_cook_food_goods ORDER BY createTime DESC") + suspend fun getAllCookFoodGoods(): MutableList + + /** 分页查询食材记录,showDel=true 时包含已删除记录 */ + @Query("SELECT * FROM dm_cook_food_goods WHERE (:showDel = 1 OR isDel = 0) ORDER BY createTime DESC LIMIT :limit OFFSET :offset") + suspend fun getCookFoodGoodsPaged(showDel: Int, limit: Int, offset: Int): List + + /** 统计食材记录总数,showDel=true 时包含已删除记录 */ + @Query("SELECT COUNT(*) FROM dm_cook_food_goods WHERE (:showDel = 1 OR isDel = 0)") + suspend fun countCookFoodGoodsAll(showDel: Int): Int + + /** 物理清空全表,用于清除测试数据 */ + @Query("DELETE FROM dm_cook_food_goods") + suspend fun deleteAll() + + /** 动态条件分页查询食材记录,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List + + /** 动态条件统计食材记录总数,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun countFiltered(query: SupportSQLiteQuery): Int +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt index 105e069..ccb3b86 100644 --- a/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningDao.kt @@ -1,25 +1,70 @@ -//package com.shuwei.dish.match.db.dao -// -//import androidx.room.Dao -//import androidx.room.Query -//import com.shuwei.dish.match.db.BaseDao -//import com.shuwei.dish.match.entity.SeasoningEntity -//import kotlinx.coroutines.flow.Flow -// -//@Dao -//interface SeasoningDao : BaseDao { -// -//// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND id = :id") -//// suspend fun getSeasoningById(id: Long): SeasoningEntity? -//// -//// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 ORDER BY sort ASC") -//// fun getAllStream(): Flow> -//// -//// @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND name LIKE '%' || :query || '%'") -//// suspend fun search(query: String): MutableList -//// -//// @Query("DELETE FROM dm_seasoning WHERE rowid IN " + -//// "(SELECT rowid FROM dm_seasoning LIMIT 1000)") -//// suspend fun deleteBatch(): Int -// -//} \ No newline at end of file +package com.shuwei.dish.match.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.RawQuery +import androidx.room.Update +import androidx.sqlite.db.SupportSQLiteQuery +import com.shuwei.dish.match.db.entity.SeasoningEntity + +@Dao +interface SeasoningDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertSeasoning(item: SeasoningEntity): Long + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertSeasoningList(items: MutableList): Array + + @Update + suspend fun updateSeasoning(item: SeasoningEntity) + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND id = :id") + suspend fun getSeasoningById(id: Long): SeasoningEntity? + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND sort = :sort") + suspend fun getSeasoningBySort(sort: Int): MutableList? + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId = :goodsId") + suspend fun getSeasoningByGoodsId(goodsId: String): SeasoningEntity? + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsId != -1 AND goodsName != '' ORDER BY sort ASC") + fun getAllStream(): MutableList + + @Query("SELECT * FROM dm_seasoning WHERE isDel = 0 AND goodsName LIKE '%' || :query || '%'") + suspend fun search(query: String): MutableList + + // @Query("DELETE FROM dm_seasoning WHERE isDel = 0 AND sort = :sort") + @Query("UPDATE dm_seasoning SET isDel = 1 WHERE isDel = 0 AND sort = :sort") + suspend fun deleteSeasoningBySort(sort: Int): Int + + // @Query("DELETE FROM dm_seasoning WHERE isDel = 0") + @Query("UPDATE dm_seasoning SET isDel = 1 WHERE isDel = 0") + suspend fun clearAllSeasoning(): Int + + /** 查询全部调料记录(含已删除),用于数据库调试查看 */ + @Query("SELECT * FROM dm_seasoning ORDER BY sort ASC") + suspend fun getAllSeasoning(): MutableList + + /** 分页查询调料记录,showDel=true 时包含已删除记录 */ + @Query("SELECT * FROM dm_seasoning WHERE (:showDel = 1 OR isDel = 0) ORDER BY sort ASC LIMIT :limit OFFSET :offset") + suspend fun getSeasoningPaged(showDel: Int, limit: Int, offset: Int): List + + /** 统计调料记录总数,showDel=true 时包含已删除记录 */ + @Query("SELECT COUNT(*) FROM dm_seasoning WHERE (:showDel = 1 OR isDel = 0)") + suspend fun countSeasoningAll(showDel: Int): Int + + /** 物理清空全表,用于清除测试数据(比 clearAllSeasoning 的逻辑删除更彻底) */ + @Query("DELETE FROM dm_seasoning") + suspend fun deleteAll() + + /** 动态条件分页查询调料记录,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List + + /** 动态条件统计调料记录总数,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun countFiltered(query: SupportSQLiteQuery): Int +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningSlotDao.kt b/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningSlotDao.kt new file mode 100644 index 0000000..af2baba --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/dao/SeasoningSlotDao.kt @@ -0,0 +1,77 @@ +package com.shuwei.dish.match.db.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.RawQuery +import androidx.sqlite.db.SupportSQLiteQuery +import com.shuwei.dish.match.db.entity.SeasoningSlotEntity + +/** + * 调料槽位配置 DAO + * 以 (deviceId, address) 为主键做 upsert,保证同一槽位只保留最新配置 + */ +@Dao +interface SeasoningSlotDao { + + /** + * 插入或更新单个槽位配置(主键冲突时覆盖) + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun upsert(slot: SeasoningSlotEntity) + + /** + * 批量插入或更新槽位配置(主设备广播全量时使用) + */ + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun upsertAll(slots: List) + + /** + * 查询指定设备的全部槽位配置 + * @param deviceId 目标设备 ID + */ + @Query("SELECT * FROM dm_seasoning_slot WHERE deviceId = :deviceId") + suspend fun queryByDeviceId(deviceId: String): List + + /** + * 查询所有设备的全部槽位配置(主设备广播时使用) + */ + @Query("SELECT * FROM dm_seasoning_slot") + suspend fun queryAll(): List + + /** 分页查询槽位记录(槽位表无 isDel,showDel 参数忽略) */ + @Query("SELECT * FROM dm_seasoning_slot ORDER BY deviceId ASC, address ASC LIMIT :limit OFFSET :offset") + suspend fun getSlotPaged(limit: Int, offset: Int): List + + /** 统计槽位记录总数 */ + @Query("SELECT COUNT(*) FROM dm_seasoning_slot") + suspend fun countSlotAll(): Int + + /** + * 删除指定设备的指定槽位配置 + * @param deviceId 目标设备 ID + * @param address 秤硬件地址 + */ + @Query("DELETE FROM dm_seasoning_slot WHERE deviceId = :deviceId AND address = :address") + suspend fun deleteByDeviceIdAndAddress(deviceId: String, address: Int) + + /** + * 删除指定设备的全部槽位配置(子设备接收全量广播时先清空再写入) + * @param deviceId 目标设备 ID + */ + @Query("DELETE FROM dm_seasoning_slot WHERE deviceId = :deviceId") + suspend fun deleteAllByDeviceId(deviceId: String) + + /** 物理清空全表,用于清除测试数据 */ + @Query("DELETE FROM dm_seasoning_slot") + suspend fun deleteAll() + + /** 动态条件分页查询槽位记录,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun queryPagedFiltered(query: SupportSQLiteQuery): List + + /** 动态条件统计槽位记录总数,由 ViewModel 构建 SQL */ + @RawQuery + suspend fun countFiltered(query: SupportSQLiteQuery): Int +} diff --git a/app/src/main/java/com/shuwei/dish/match/entity/CookFoodEntity.kt b/app/src/main/java/com/shuwei/dish/match/db/entity/CookFoodEntity.kt similarity index 64% rename from app/src/main/java/com/shuwei/dish/match/entity/CookFoodEntity.kt rename to app/src/main/java/com/shuwei/dish/match/db/entity/CookFoodEntity.kt index 7830103..d084da3 100644 --- a/app/src/main/java/com/shuwei/dish/match/entity/CookFoodEntity.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/entity/CookFoodEntity.kt @@ -1,4 +1,4 @@ -package com.shuwei.dish.match.entity +package com.shuwei.dish.match.db.entity import androidx.room.Entity import androidx.room.Ignore @@ -18,11 +18,17 @@ data class CookFoodEntity( //0-制作模式,1-采样模式,2-品控模式 var cookMode:Int = 0, var isDel: Int = 0, + /** 接口提交状态:0-未提交(本地草稿或手动删除),1-接口提交成功后删除 */ + var submitState: Int = 0, var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()), var dinnerType: String? = "0", ) : BaseEntity { +// @Ignore +// var stFoodInfoConstituteList: MutableList? = null @Ignore - var stFoodInfoConstituteList: MutableList? = null + var matchingConstituteInfoList: MutableList? = null + @Ignore + var foodConstituteList: MutableList? = null constructor() : this(foodId = "") // 必需的空构造 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/shuwei/dish/match/entity/CookFoodGoodsEntity.kt b/app/src/main/java/com/shuwei/dish/match/db/entity/CookFoodGoodsEntity.kt similarity index 52% rename from app/src/main/java/com/shuwei/dish/match/entity/CookFoodGoodsEntity.kt rename to app/src/main/java/com/shuwei/dish/match/db/entity/CookFoodGoodsEntity.kt index d415744..c358c3e 100644 --- a/app/src/main/java/com/shuwei/dish/match/entity/CookFoodGoodsEntity.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/entity/CookFoodGoodsEntity.kt @@ -1,9 +1,9 @@ -package com.shuwei.dish.match.entity +package com.shuwei.dish.match.db.entity import androidx.room.Entity -import androidx.room.Ignore import androidx.room.PrimaryKey import com.shuwei.dish.match.db.BaseEntity +import com.shuwei.dish.match.model.GoodsItem import com.shuwei.dish.match.utils.DateTimeUtil import java.io.Serializable import java.time.LocalDateTime @@ -43,10 +43,14 @@ class CookFoodGoodsEntity( */ var materialType: Int = 0, +// /** +// * 物品 是否全部可食:1-全部可食,0-部分可食,默认全部 +// */ +// var allEdible: Int = 1, /** - * 物品 是否全部可食:1-全部可食,0-部分可食,默认全部 + * 物品 是否全部可食:true-全部可食,false-部分可食,默认全部 */ - var allEdible: Int = 1, + var allEdible: Boolean = true, /** * 物品 用料重量 @@ -59,23 +63,36 @@ class CookFoodGoodsEntity( var relateionType_dictText: String? = "", + var zjmCode: String? = "", + + var materId: String? = "", + + /** 物料编码 */ + var goodsCode: String? = "", + + /** 净材种类 */ + var rawMaterialsType: String? = null, + var isDel: Int = 0, var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()) ) : BaseEntity, Serializable { - constructor() : this(goodsId = "") // 必需的空构造, + constructor() : this(goodsId = "") // 必需的空构造 - @Ignore - var isSetFinished: Boolean = false - - @Ignore - var isItemClicked: Boolean = false - - @Ignore - var isSamplingPage: Boolean = false - - @Ignore - var isNewDishType: Boolean = false - - @Ignore - var isClicked: Boolean = false -} \ No newline at end of file + /** + * 转换为 GoodsItem,用于 UI 层(DishPartAdapter、PrepareFoodActivity 等) + */ + fun toGoodsItem(): GoodsItem = GoodsItem( + goodsId = goodsId, + goodsName = goodsName, + popularName = popularName, + zjmCode = zjmCode, + materId = materId, + materCode = goodsCode, + materialType = materialType, + useWeight = useWeight, + relateionType = relateionType, + allEdible = allEdible, + goodsOrRelationCode = goodsOrRelationCode, + rawMaterialsType = rawMaterialsType + ) +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/entity/ResetReasoningRecord.kt b/app/src/main/java/com/shuwei/dish/match/db/entity/ResetReasoningRecord.kt new file mode 100644 index 0000000..8d08e8c --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/entity/ResetReasoningRecord.kt @@ -0,0 +1,6 @@ +package com.shuwei.dish.match.db.entity + +/** 默认调料配置 JSON 反序列化的包装类 */ +data class ResetReasoningRecord( + val list: MutableList = mutableListOf() +) diff --git a/app/src/main/java/com/shuwei/dish/match/entity/SeasoningEntity.kt b/app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningEntity.kt similarity index 59% rename from app/src/main/java/com/shuwei/dish/match/entity/SeasoningEntity.kt rename to app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningEntity.kt index 7ea8dbe..c8f6c06 100644 --- a/app/src/main/java/com/shuwei/dish/match/entity/SeasoningEntity.kt +++ b/app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningEntity.kt @@ -1,4 +1,4 @@ -package com.shuwei.dish.match.entity +package com.shuwei.dish.match.db.entity import androidx.room.Entity import androidx.room.Ignore @@ -36,11 +36,14 @@ data class SeasoningEntity( */ var materialType: Int = 3, +// /** +// * 物品 是否全部可食:1-全部可食,0-部分可食,默认全部 +// */ +// var allEdible: Int = 1, /** - * 物品 是否全部可食:1-全部可食,0-部分可食,默认全部 + * 物品 是否全部可食:true-全部可食,false-部分可食,默认全部 */ - var allEdible: Int = 1, - + var allEdible: Boolean = true, /** * 物品 用料重量 */ @@ -52,18 +55,27 @@ data class SeasoningEntity( var relateionType_dictText: String? = "", + var zjmCode: String? = "", + + var materId: String? = "", + + /** 物料编码 */ + var goodsCode: String? = "", + + /** 净材种类 */ + var rawMaterialsType: String? = null, + var sort: Int = 9999, /** - * isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据 - */ - var isDel: Int = 0, + * isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据 +*/ +var isDel: Int = 0, var createTime: String = DateTimeUtil.formatDateTime(LocalDateTime.now()) ) : BaseEntity { - @Ignore - var pageType: Int = 0 - - @Ignore - var isClicked: Boolean = false + /** UI 状态:格子类型,非数据库字段 */ + @Ignore var pageType: Int = 0 + /** UI 状态:是否选中,非数据库字段 */ + @Ignore var isClicked: Boolean = false constructor() : this(goodsId = "") // 必需的空构造 -} \ No newline at end of file +} diff --git a/app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningSlotEntity.kt b/app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningSlotEntity.kt new file mode 100644 index 0000000..cad577e --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/db/entity/SeasoningSlotEntity.kt @@ -0,0 +1,23 @@ +package com.shuwei.dish.match.db.entity + +import androidx.room.Entity + +/** + * 调料槽位配置表,记录每个格子对应哪种调料 + * 主键为 (deviceId, address) 组合,唯一标识一个物理秤槽位 + * + * @param deviceId 所属设备 ID,用于区分 22格/18格 设备 + * @param address 秤硬件地址,对应 ScaleDeviceConfig.SCALE_ORDER_22/18 中的值 + * @param goodsId 调料 ID + * @param goodsName 调料名称,用于 adapter 展示 + */ +@Entity( + tableName = "dm_seasoning_slot", + primaryKeys = ["deviceId", "address"] +) +data class SeasoningSlotEntity( + val deviceId: String, + val address: Int, + val goodsId: String, + val goodsName: String +) diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt index f4a3089..89d6dff 100644 --- a/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt +++ b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog.kt @@ -16,14 +16,13 @@ import com.shuwei.dish.match.adapter.GoodsInfoSearchAdapter import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.DialogBottomSheetBinding -import com.shuwei.dish.match.entity.CookFoodGoodsEntity -import com.shuwei.dish.match.entity.GoodsRecordBean -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.model.GoodsItem +import com.shuwei.dish.match.net.RemoteRepository +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.KeyboardUtil import com.shuwei.dish.match.utils.ext.addOnActionSearchListener -import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch import com.shuwei.dish.match.utils.ext.toast /** @@ -37,7 +36,7 @@ class BottomDialog( const val TAG = "BottomDialog" } - private var list = mutableListOf() + private var list = mutableListOf() private lateinit var binding: DialogBottomSheetBinding private lateinit var goodsInfoSearchAdapter: GoodsInfoSearchAdapter @@ -45,7 +44,7 @@ class BottomDialog( private var activity: BaseActivity? = null - fun show(activity: BaseActivity, callback: (item: CookFoodGoodsEntity) -> Unit) { + fun show(activity: BaseActivity, callback: (item: GoodsItem) -> Unit) { this.activity = activity //直接使用BottomSheetDialog var dialog: BottomSheetDialog? = null @@ -142,59 +141,52 @@ class BottomDialog( */ @SuppressLint("NotifyDataSetChanged") private fun getGoodsList() { -// val pathSegmentList = UrlConfig.QUERY_GOODS_LIST.split(delimiters = arrayOf("/"), limit = 1) - val queryParams = mutableMapOf( + val param = mutableMapOf( "goodsType" to "$goodsType", - "canteenId" to BaseApp.canteenId, - "pageNo" to "$pageNo", - "pageSize" to "$pageSize" + "placeId" to BaseApp.canteenId, + "pageNum" to pageNo, + "pageSize" to pageSize ) if (goodsName.isNullOrBlank().not()) { - queryParams.put("name", goodsName!!) + param["goodsName"] = goodsName!! } - val sb = StringBuilder().apply { - append(UrlConfig.QUERY_GOODS_LIST) - append("?") - queryParams.forEach { (key, value) -> - append("$key=$value&") + Log.d(TAG, "getGoodsList: param=$param") + activity?.lifecycleScope?.launch { + val state = RemoteRepository().queryGoodsList(param) + when (state) { + is UiState.Success -> { + val records = state.data + if (records.isNullOrEmpty()) { + activity?.toast(if (goodsType == 1) "暂未搜索到调料信息" else "暂未搜索到食材信息") + return@launch + } + if (pageNo == 1) { + list.clear() + } + list.addAll(records) + goodsInfoSearchAdapter.notifyDataSetChanged() + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } + } + is UiState.Error -> { + activity?.toast(state.msg) + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + } + else -> {} } } - sb.deleteCharAt(sb.length - 1) - Log.d(TAG, "getGoodsList: url = $sb") - HttpUtil.get( - url = sb.toString(), - doSuccess = { - val json = it.toJsonString() - Log.d(TAG, "getGoodsList: json=$json") - val recordBean: GoodsRecordBean? = json.toObject() - if (recordBean == null || recordBean.records.isNullOrEmpty()) { - activity?.toast(if (goodsType == 1) "暂未搜索到调料信息" else "暂未搜索到食材信息") - return@get - } - val records = recordBean.records - if (pageNo == 1) { - list.clear() - } - list.addAll(records!!) - goodsInfoSearchAdapter.notifyDataSetChanged() - if (pageNo == 1) { - binding.refreshLayout.finishRefresh(1200) - } else { - binding.refreshLayout.finishLoadMore(1200) - } - val isLoadMoreEnable = records.size >= pageSize - binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) - if (isLoadMoreEnable) { - pageNo++ - } - }, doFailure = { code, msg -> - activity?.toast(msg) - if (pageNo == 1) { - binding.refreshLayout.finishRefresh(1200) - } else { - binding.refreshLayout.finishLoadMore(1200) - } - }) } } diff --git a/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt index 159033c..6cd581e 100644 --- a/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt +++ b/app/src/main/java/com/shuwei/dish/match/dialog/BottomDialog2.kt @@ -23,20 +23,19 @@ import com.shuwei.dish.match.adapter.SeasoningSearchAdapter import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.DialogBottomSheet2Binding -import com.shuwei.dish.match.entity.SeasoningEntity -import com.shuwei.dish.match.entity.SeasoningRecordBean -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.model.GoodsItem +import com.shuwei.dish.match.net.RemoteRepository +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.KeyboardUtil import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.ext.addOnActionSearchListener +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.launch import com.shuwei.dish.match.utils.ext.appendText import com.shuwei.dish.match.utils.ext.buildSpannableString import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.ext.roundedDecimalPlace import com.shuwei.dish.match.utils.ext.roundedOneDecimalPlace -import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toast /** @@ -52,7 +51,7 @@ class BottomDialog2( } private val goodsType: Int = 1 - private var list = mutableListOf() + private var list = mutableListOf() private lateinit var binding: DialogBottomSheet2Binding private lateinit var searchAdapter: SeasoningSearchAdapter @@ -60,7 +59,7 @@ class BottomDialog2( private var activity: BaseActivity? = null - fun show(activity: BaseActivity, callback: (item: SeasoningEntity) -> Unit) { + fun show(activity: BaseActivity, callback: (item: GoodsItem) -> Unit) { this.activity = activity //直接使用BottomSheetDialog var dialog: BottomSheetDialog? = null @@ -173,58 +172,52 @@ class BottomDialog2( */ @SuppressLint("NotifyDataSetChanged") private fun getGoodsList() { - val queryParams = mutableMapOf( + val param = mutableMapOf( "goodsType" to "$goodsType", - "canteenId" to BaseApp.canteenId, - "pageNo" to "$pageNo", - "pageSize" to "$pageSize" + "placeId" to BaseApp.canteenId, + "pageNum" to pageNo, + "pageSize" to pageSize ) if (goodsName.isNullOrBlank().not()) { - queryParams.put("name", goodsName!!) + param["goodsName"] = goodsName!! } - val sb = StringBuilder().apply { - append(UrlConfig.QUERY_GOODS_LIST) - append("?") - queryParams.forEach { (key, value) -> - append("$key=$value&") + Log.d(TAG, "getGoodsList: param=$param") + activity?.lifecycleScope?.launch { + val state = RemoteRepository().queryGoodsList(param) + when (state) { + is UiState.Success -> { + val records = state.data + if (records.isNullOrEmpty()) { + activity?.toast(if (goodsType == 1) "暂未搜索到调料信息" else "暂未搜索到食材信息") + return@launch + } + if (pageNo == 1) { + list.clear() + } + list.addAll(records) + searchAdapter.notifyDataSetChanged() + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } + } + is UiState.Error -> { + activity?.toast(state.msg) + if (pageNo == 1) { + binding.refreshLayout.finishRefresh(1200) + } else { + binding.refreshLayout.finishLoadMore(1200) + } + } + else -> {} } } - sb.deleteCharAt(sb.length - 1) - Log.d(TAG, "getGoodsList: url = $sb") - HttpUtil.get( - url = sb.toString(), - doSuccess = { - val json = it.toJsonString() - Log.d(TAG, "getGoodsList: json=$json") - val recordBean: SeasoningRecordBean? = json.toObject() - if (recordBean == null || recordBean.records.isNullOrEmpty()) { - activity?.toast(if (goodsType == 1) "暂未搜索到调料信息" else "暂未搜索到食材信息") - return@get - } - val records = recordBean.records - if (pageNo == 1) { - list.clear() - } - list.addAll(records!!) - searchAdapter.notifyDataSetChanged() - if (pageNo == 1) { - binding.refreshLayout.finishRefresh(1200) - } else { - binding.refreshLayout.finishLoadMore(1200) - } - val isLoadMoreEnable = records.size >= pageSize - binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) - if (isLoadMoreEnable) { - pageNo++ - } - }, doFailure = { code, msg -> - activity?.toast(msg) - if (pageNo == 1) { - binding.refreshLayout.finishRefresh(1200) - } else { - binding.refreshLayout.finishLoadMore(1200) - } - }) } fun getTextSpan(weight: Double): SpannableStringBuilder { diff --git a/app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt b/app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt deleted file mode 100644 index 153e651..0000000 --- a/app/src/main/java/com/shuwei/dish/match/entity/DataBean.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.shuwei.dish.match.entity - -data class HomeModeBean( - - var modeName: String = "", - var modeDesc: String = "", - var modeIconId: Int = 0, - var isLocked: Boolean = false, - - var onClick: (() -> Unit)? = null -) - -data class FoodRecordBean( - var records: MutableList? = null, - var total: Int, - var size: Int, - var current: Int, - var page: Int -) - -data class GoodsRecordBean( - var records: MutableList? = null, - var total: Int, - var size: Int, - var current: Int, - var page: Int -) - -data class SeasoningRecordBean( - var records: MutableList? = null, - var total: Int, - var size: Int, - var current: Int, - var page: Int -) - -data class ResetReasoningRecord( - val list: MutableList -) - -//data class DishDetailEntity( -// var foodId: String? = null, -// var foodWeight: Double? = null, -// var foodRealWeight: Double? = null, -// var voList: List? = null -//) - -//data class DishPartEntity( -// var foodId: String? = null, -// var foodName: String? = null, -// var foodWeight: Int? = null, -// var useWeight: Int? = null, -// var useRealWeight: Int? = null, -// var goodsId: Int? = null, -// var goodsName: String? = null, -// var materialType: Int? = null, -// var isSetFinished: Boolean = false, -// var isItemClicked: Boolean = false, -// var isSamplingPage: Boolean = false -//) - - diff --git a/app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt deleted file mode 100644 index 0c7c247..0000000 --- a/app/src/main/java/com/shuwei/dish/match/entity/DishEntity.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.shuwei.dish.match.entity - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import com.shuwei.dish.match.db.BaseEntity - -//@Entity(tableName = "dm_dish") -//data class DishEntity ( -// /** -// * 菜品id -// */ -// @PrimaryKey var id: Long, -// /** -// * 菜品名称 -// */ -// var name: String? = "", -// /** -// * 菜品重量 -// */ -// var weight: Int? = 0, -// -// var sort: Int = 0, -// @ColumnInfo(name = "create_time") val createTime: Long = System.currentTimeMillis() -//): BaseEntity \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt b/app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt deleted file mode 100644 index 20c2816..0000000 --- a/app/src/main/java/com/shuwei/dish/match/entity/GoodsInfoEntity.kt +++ /dev/null @@ -1,38 +0,0 @@ -//package com.shuwei.dish.match.entity -// -//import androidx.room.Ignore -//import androidx.room.PrimaryKey -//import com.shuwei.dish.match.db.BaseEntity -// -//data class GoodsInfoEntity( -// @PrimaryKey(autoGenerate = true) var id: Long = 0, -// var goodsId: Int, -// var goodsName: String? = "", -// var popularName: String? = "", -// var canteenId: String? = "", -// var goodsOrRelationCode: String? = "", -// var relateionType: Int? = -1, -// var relateionType_dictText: String? = "", -// -// var sort: Int = 0, -// -// var swMaterBaseMaterial: String? = "", -// var preMaterClass: String? = "", -// var preUseWeight: Int? = 0, -// var preWeight: Int? = 0, -// var oil: String? = "", -// var sugar: String? = "", -// /** -// * isDel表示数据是否已删除,1-已删除,为无效数据,0-正常使用,有效数据 -// */ -// var isDel: Int = 0, -// val createTime: Long = System.currentTimeMillis() -//) : BaseEntity { -// @Ignore -// var pageType: Int = 0 -// -// @Ignore -// var isClicked: Boolean = false -// -// constructor() : this(id = -1, goodsId = -1) // 必需的空构造 -//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt b/app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt deleted file mode 100644 index 23d0d69..0000000 --- a/app/src/main/java/com/shuwei/dish/match/http/HttpUtil.kt +++ /dev/null @@ -1,199 +0,0 @@ -package com.shuwei.dish.match.http - -import android.os.Handler -import android.os.Looper -import android.util.Log -import com.google.gson.reflect.TypeToken -import com.shuwei.dish.match.base.BaseApp -import com.shuwei.dish.match.base.BaseReq -import com.shuwei.dish.match.utils.SpTool -import com.shuwei.dish.match.utils.ext.toType -import okhttp3.Call -import okhttp3.Callback -import okhttp3.MediaType.Companion.toMediaTypeOrNull -import okhttp3.Request -import okhttp3.RequestBody.Companion.toRequestBody -import okhttp3.Response -import java.io.IOException -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit - - -object HttpUtil { - - var loopGetToken = true - - fun runMainThread(action: () -> Unit) { - Handler(Looper.getMainLooper()).post { - action() - } - } - - // GET请求(HTTPS) - fun get( - url: String, - header: Map?=null, - doSuccess: (data: Any) -> Unit, - doFailure: (code: Int?, msg: String?) -> Unit - ) { - val request = Request.Builder() - .url(if (url.isHttpApi()) url else UrlConfig.BASE_URL + url) - .apply { - if (header != null) { - header.forEach { - addHeader(it.key, it.value) - } - } else { - val token = getToken() - if (token.isNotBlank()) { - addHeader("X-Access-Token", token) - } - } - } - .get() - .build() - HttpClient.instance.newCall(request).enqueue(CallbackImpl(doSuccess, doFailure)) - } -// -// fun get( -// isHttps: Boolean = false, -// host: String = "vip.shuziweidao.com", -// pathSegmentList: List, -// queryParams: Map, -// doSuccess: (data: Any) -> Unit, -// doFailure: (code: Int?, msg: String?) -> Unit -// ) { -// val token = getToken() -// val url = HttpUrl.Builder() -// .scheme(if (isHttps) "https" else "http") -// .host(host) -// .apply { -// pathSegmentList.forEach { addPathSegment(it) } -// queryParams.forEach { (key, value) -> addQueryParameter(key, value) } -// } -// .build() -// val request = Request.Builder() -// .url(url) -// .apply { -// if (token.isNotBlank()) { -// addHeader("Authorization", token) -// } -// } -// .get() -// .build() -// HttpClient.instance.newCall(request).enqueue(CallbackImpl(doSuccess, doFailure)) -// } - - // POST表单(HTTPS) -// fun postForm(url: String, params: Map, callback: Callback) { -// val formBody = FormBody.Builder().apply { -// params.forEach { (k, v) -> add(k, v) } -// }.build() -// -// Request.Builder() -// .url(url) -// .post(formBody) -// .build().let { HttpClient.instance.newCall(it).enqueue(callback) } -// } - - // POST JSON(HTTPS) - fun postJson( - url: String, - json: String, - doSuccess: (data: Any) -> Unit, - doFailure: (code: Int?, msg: String?) -> Unit - ) { - val body = json - .toRequestBody("application/json; charset=utf-8".toMediaTypeOrNull()) - val token = getToken() - Request.Builder() - .url(if (url.isHttpApi()) url else UrlConfig.BASE_URL + url) - .apply { - if (token.isNotBlank()) { - addHeader("X-Access-Token", token) - } - } - .post(body) - .build() - .let { HttpClient.instance.newCall(it).enqueue(CallbackImpl(doSuccess, doFailure)) } - } - - - val executor = Executors.newSingleThreadScheduledExecutor() - var getTokenIsRunning = false - private fun exeTokenThread() { - if (getTokenIsRunning.not()) { - executor.scheduleWithFixedDelay( - task, 0, 1, TimeUnit.MINUTES - ) - getTokenIsRunning = true - } - } - - val task = Runnable { - if (BaseApp.token.isNullOrBlank()) { - getAppToken() - } else { - executor.shutdown() - } - } - - fun getToken() = SpTool.getString(SpTool.TOKEN) -//BaseApp.instance?.token ?:"" -//BaseApp.getSharedPref()?.getString("token", "") ?: "" - - fun getAppToken() { - val url = "${UrlConfig.GET_TOKEN}?qrcodeId=${SpTool.getString(SpTool.DEVICE_ID)}" -// Log.d(TAG, "getToken: url = $url") - get(url = url, doSuccess = { - SpTool.put(SpTool.TOKEN, it) - BaseApp.token = it.toString() - }) { code, msg -> - //toast(msg) - } - } - - class CallbackImpl( - private val doSuccess: (data: Any) -> Unit, - private val doFailure: (code: Int?, msg: String?) -> Unit - ) : - Callback { - override fun onFailure(call: Call, e: IOException) { - runMainThread { - e.printStackTrace() - doFailure(-1, "服务异常,${e.message}") - } - } - - override fun onResponse(call: Call, response: Response) { - val respData = response.body?.string() - runMainThread { - Log.d("HttpUtil", respData ?: "") - runCatching { - val typeToken = object : TypeToken>() {} - val baseReq = respData?.toType>(typeToken = typeToken) - if (baseReq == null) { - doFailure(-1, "查询数据失败") - return@runCatching - } - if (baseReq.code != 200) { - doFailure(baseReq.code, baseReq.message) - //baseReq.code == 500 || - if (baseReq.code == 401 && loopGetToken) { - exeTokenThread() - } - return@runCatching - } - doSuccess(baseReq.result ?: "") - }.onFailure { - it.printStackTrace() - doFailure(-1, "解析异常") - } - } - } - - } - -} - -fun String.isHttpApi() = this.startsWith("http://") || this.startsWith("https://") diff --git a/app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt b/app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt deleted file mode 100644 index 0139639..0000000 --- a/app/src/main/java/com/shuwei/dish/match/http/UrlConfig.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.shuwei.dish.match.http - -import com.shuwei.dish.match.base.BaseApp - -object UrlConfig { - private const val DEVICE_BASE_URL = "http://device.shuziweidao.com:8889" - /** - * device获取token - */ - const val DEVICE_TOKEN = "${DEVICE_BASE_URL}/sys/getEquipmentToken" - /** - *获取配置信息 - */ - const val DEVICE_CONFIG = "${DEVICE_BASE_URL}/equipment/stEquipment/queryByEquipmentCode" - -// const val BASE_URL = "http://vip.shuziweidao.com/shuwei-zhct" -// const val DISH_DETAIL = "$BASE_URL/scales/goodsUseList?foodId={foodId}&foodWeight={foodWeight}" -// const val DISH_LIST = "$BASE_URL/scales/getRestInfoFoods?eaId=99&type=0&foodName" - -// const val BASE_URL = "http://192.168.1.207:9102" - const val BASE_URL = "https://yyjk.shuziweidao.com/gateway" -// const val DISH_LIST = "$BASE_URL/scales/getRestInfoFoods?eaId=99&type=0&foodName" - - - var DISH_DETAIL = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryById" - - // "$BASE_URL/scales/generateToken?deviceId=1111111111111111111111111111" - - var GET_TOKEN = "${BaseApp.configUrl}/restaurant/equipment/stEquipment/getEquipmentToken" - var SUBMIT_DISH = "${BaseApp.configUrl}/food/stFoodInfoMatching/saveoredit" - - var QUERY_GOODS_LIST = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryGoodsInfoList" - var QUERY_FOOD_LIST = "${BaseApp.configUrl}/food/stFoodInfoMatching/list" - var SAMPLING_LIST = "${BaseApp.configUrl}/food/stFoodInfoMatching/queryHistorical/goodsInfoList" - -} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/model/CollectedFoodItem.kt b/app/src/main/java/com/shuwei/dish/match/model/CollectedFoodItem.kt new file mode 100644 index 0000000..72c33e8 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/CollectedFoodItem.kt @@ -0,0 +1,11 @@ +package com.shuwei.dish.match.model + +/** + * 已采集食材列表项数据模型 + * @param foodName 食材名称 + * @param collectCount 已采集数量 + */ +data class CollectedFoodItem( + val foodName: String = "", + val collectCount: Int = 0 +) diff --git a/app/src/main/java/com/shuwei/dish/match/model/CookFoodDTO.kt b/app/src/main/java/com/shuwei/dish/match/model/CookFoodDTO.kt new file mode 100644 index 0000000..d89059a --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/CookFoodDTO.kt @@ -0,0 +1,43 @@ +package com.shuwei.dish.match.model + +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import java.io.Serializable + +/** + * 菜品详情接口传输对象(DTO),不依赖 Room + * 对应接口:getConstituteByFoodId / saveConstitute + */ +data class CookFoodDTO( + var foodId: String = "", + var foodName: String? = null, + var canteenId: String? = null, + var foodWeight: Double = 0.0, + // 0-制作模式,1-采样模式,2-品控模式 + var cookMode: Int = 0, + var dinnerType: String? = "0", + // 接口返回的食材构成列表 + var matchingConstituteInfoList: MutableList? = null, + var foodConstituteList: MutableList? = null +) : Serializable { + + /** + * 转换为 Room 实体,用于写入本地数据库 + * id/isDel/createTime 由 Room 自行管理,不从 DTO 携带 + */ + fun toEntity(): CookFoodEntity = CookFoodEntity( + foodId = foodId, + foodName = foodName, + canteenId = canteenId, + foodWeight = foodWeight, + cookMode = cookMode, + dinnerType = dinnerType + ).also { entity -> + entity.matchingConstituteInfoList = matchingConstituteInfoList + ?.map { it.toEntity() } + ?.toMutableList() + entity.foodConstituteList = foodConstituteList + ?.map { it.toEntity() } + ?.toMutableList() + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/model/CookFoodGoodsDTO.kt b/app/src/main/java/com/shuwei/dish/match/model/CookFoodGoodsDTO.kt new file mode 100644 index 0000000..8e493c1 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/CookFoodGoodsDTO.kt @@ -0,0 +1,119 @@ +package com.shuwei.dish.match.model + +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import java.io.Serializable + +/** + * 菜品构成(主辅料/调料)接口传输对象(DTO),不依赖 Room + * 对应 CookFoodDTO 中 foodConstituteList、matchingConstituteInfoList 的元素 + */ +data class CookFoodGoodsDTO( + var goodsId: String = "", + var goodsName: String? = null, + var foodId: String? = "", + var goodsOrRelationCode: String? = "", + // 调料数据顺序 + var sort: Int = 0, + // 食材原材料:1,预制品:2 + var relateionType: Int = 0, + // 物品类型:1主料 2辅料 3调料 + var materialType: Int = 0, + // 物品 是否全部可食:true-全部可食,false-部分可食 + var allEdible: Boolean = true, + // 物品 用料重量 + var useWeight: Double? = 0.0, + var popularName: String? = "", + var canteenId: String? = "", + var relateionType_dictText: String? = "", + var zjmCode: String? = "", + var materId: String? = "", + // 物料编码 + var goodsCode: String? = "", + // 净材种类 + var rawMaterialsType: String? = null +) : Serializable { + + /** + * 转换为 GoodsItem,用于 UI 层 + */ + fun toGoodsItem(): GoodsItem = GoodsItem( + goodsId = goodsId, + goodsName = goodsName, + popularName = popularName, + zjmCode = zjmCode, + materId = materId, + materCode = goodsCode, + materialType = materialType, + useWeight = useWeight, + relateionType = relateionType, + allEdible = allEdible, + goodsOrRelationCode = goodsOrRelationCode, + rawMaterialsType = rawMaterialsType + ) + + /** + * 转换为 Room 实体,用于写入本地数据库 + * id/pid/isDel/createTime 由 Room 自行管理,不从 DTO 携带 + */ + fun toEntity(): CookFoodGoodsEntity = CookFoodGoodsEntity( + goodsId = goodsId, + goodsName = goodsName, + foodId = foodId, + goodsOrRelationCode = goodsOrRelationCode, + sort = sort, + relateionType = relateionType, + materialType = materialType, + allEdible = allEdible, + useWeight = useWeight, + popularName = popularName, + canteenId = canteenId, + relateionType_dictText = relateionType_dictText, + zjmCode = zjmCode, + materId = materId, + goodsCode = goodsCode, + rawMaterialsType = rawMaterialsType + ) +} + +/** + * Room 实体 → DTO 反向映射,用于将本地数据转为接口提交格式 + * id/pid/isDel/createTime 为本地数据库字段,不参与提交 + */ +fun CookFoodGoodsEntity.toDTO(): CookFoodGoodsDTO = CookFoodGoodsDTO( + goodsId = goodsId, + goodsName = goodsName, + foodId = foodId, + goodsOrRelationCode = goodsOrRelationCode, + sort = sort, + relateionType = relateionType, + materialType = materialType, + allEdible = allEdible, + useWeight = useWeight, + popularName = popularName, + canteenId = canteenId, + relateionType_dictText = relateionType_dictText, + zjmCode = zjmCode, + materId = materId, + goodsCode = goodsCode, + rawMaterialsType = rawMaterialsType +) + +/** + * Room 实体 → DTO 反向映射,用于将本地数据转为接口提交格式 + * id/isDel/createTime 为本地数据库字段,不参与提交 + */ +fun CookFoodEntity.toDTO(): CookFoodDTO = CookFoodDTO( + foodId = foodId, + foodName = foodName, + canteenId = canteenId, + foodWeight = foodWeight, + cookMode = cookMode, + dinnerType = dinnerType, + matchingConstituteInfoList = matchingConstituteInfoList + ?.map { it.toDTO() } + ?.toMutableList(), + foodConstituteList = foodConstituteList + ?.map { it.toDTO() } + ?.toMutableList() +) diff --git a/app/src/main/java/com/shuwei/dish/match/model/DataBean.kt b/app/src/main/java/com/shuwei/dish/match/model/DataBean.kt new file mode 100644 index 0000000..e4c41fa --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/DataBean.kt @@ -0,0 +1,52 @@ +package com.shuwei.dish.match.model + +import android.graphics.Bitmap +import android.net.Uri +import java.io.File + +data class HomeModeBean( + var modeName: String = "", + var modeDesc: String = "", + var modeIconId: Int = 0, + var isLocked: Boolean = false, + + var onClick: (() -> Unit)? = null +) + +data class FoodCollectionBean( + var imageUri: Uri? = null, + var bitmap: Bitmap? = null, + var imageFile: File? = null, + var imageVector: FloatArray? = null, + var isShowCamera: Boolean = false, + var isFinish:Boolean = false, + var uploadSuccess:Boolean = false +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as FoodCollectionBean + + if (isShowCamera != other.isShowCamera) return false + if (isFinish != other.isFinish) return false + if (uploadSuccess != other.uploadSuccess) return false + if (imageUri != other.imageUri) return false + if (bitmap != other.bitmap) return false + if (imageFile != other.imageFile) return false + if (!imageVector.contentEquals(other.imageVector)) return false + + return true + } + + override fun hashCode(): Int { + var result = isShowCamera.hashCode() + result = 31 * result + isFinish.hashCode() + result = 31 * result + uploadSuccess.hashCode() + result = 31 * result + (imageUri?.hashCode() ?: 0) + result = 31 * result + (bitmap?.hashCode() ?: 0) + result = 31 * result + (imageFile?.hashCode() ?: 0) + result = 31 * result + (imageVector?.contentHashCode() ?: 0) + return result + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/entity/FoodRecord.kt b/app/src/main/java/com/shuwei/dish/match/model/FoodRecord.kt similarity index 78% rename from app/src/main/java/com/shuwei/dish/match/entity/FoodRecord.kt rename to app/src/main/java/com/shuwei/dish/match/model/FoodRecord.kt index b02bd09..8882dd0 100644 --- a/app/src/main/java/com/shuwei/dish/match/entity/FoodRecord.kt +++ b/app/src/main/java/com/shuwei/dish/match/model/FoodRecord.kt @@ -1,4 +1,4 @@ -package com.shuwei.dish.match.entity +package com.shuwei.dish.match.model import java.io.Serializable @@ -13,7 +13,7 @@ data class FoodRecord( var cookMode: Int = 0, //1-早餐,2-午餐,3-晚餐 var dinnerType: String = "0", - + //true-原始数据,false-非原始数据 + var isOriginalData: Boolean = true, var sort: Int = 0 ) : Serializable - diff --git a/app/src/main/java/com/shuwei/dish/match/model/GoodsItem.kt b/app/src/main/java/com/shuwei/dish/match/model/GoodsItem.kt new file mode 100644 index 0000000..05eda21 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/GoodsItem.kt @@ -0,0 +1,83 @@ +package com.shuwei.dish.match.model + +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.db.entity.SeasoningEntity +import java.io.Serializable + +/** + * 物品信息接口返回的数据类(DTO),不依赖 Room,同时承载 UI 状态字段 + * 对应接口:queryGoodsInfoList,食材(goodsType=0)和调料(goodsType=1)共用此结构 + */ +data class GoodsItem( + // 接口返回字段 + var goodsId: String = "", + val goodsName: String? = null, + val popularName: String? = null, + val zjmCode: String? = null, + var materId: String? = null, + val materCode: String? = null, + + // 从 CookFoodGoodsEntity 迁移的数据字段(DishPartAdapter 显示及转换用) + var materialType: Int = 0, + var useWeight: Double? = 0.0, + var relateionType: Int = 0, + var allEdible: Boolean = true, + var goodsOrRelationCode: String? = "", + + // 从 CookFoodGoodsEntity @Ignore 迁移的 UI 状态字段 + var isSetFinished: Boolean = false, +// var isItemClicked: Boolean = false, + var isSamplingPage: Boolean = false, + var isNewDishType: Boolean = false, + var isClicked: Boolean = false, + // true-原始数据,false-非原始数据(识图新增) + var isOriginalData: Boolean = true, + // 识图匹配分数,-1 表示不显示 + var foodScore: Int = -1, + // 净材种类(当前选中值) + var rawMaterialsType: String? = null, + // 净材种类候选列表(临时字段,逗号分隔,不持久化) + var rawMaterialsTypes: String? = null, + var oldGoodsId: String? = null +) : Serializable { + + /** + * 物料编码,与查询接口返回的 materCode 同值,统一对外暴露为 goodsCode 供下游使用 + */ + val goodsCode: String? get() = materCode + + /** + * 转换为 CookFoodGoodsEntity,用于写入数据库或传给 SubmitFoodActivity + */ + fun toCookFoodGoodsEntity(): CookFoodGoodsEntity = CookFoodGoodsEntity( + goodsId = goodsId, + goodsName = goodsName, + popularName = popularName, + zjmCode = zjmCode, + materId = materId, + goodsCode = goodsCode, + materialType = materialType, + useWeight = useWeight, + relateionType = relateionType, + allEdible = allEdible, + goodsOrRelationCode = goodsOrRelationCode, + rawMaterialsType = rawMaterialsType + ) + + /** + * 转换为 SeasoningEntity,用于保存调料完整信息到 dm_seasoning + */ + fun toSeasoningEntity(): SeasoningEntity = SeasoningEntity( + goodsId = goodsId, + goodsName = goodsName, + popularName = popularName, + zjmCode = zjmCode, + materId = materId, + goodsCode = goodsCode, + materialType = materialType, + relateionType = relateionType, + allEdible = allEdible, + goodsOrRelationCode = goodsOrRelationCode, + rawMaterialsType = rawMaterialsType + ) +} diff --git a/app/src/main/java/com/shuwei/dish/match/model/GoodsNameQueryDTO.kt b/app/src/main/java/com/shuwei/dish/match/model/GoodsNameQueryDTO.kt new file mode 100644 index 0000000..8e93628 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/GoodsNameQueryDTO.kt @@ -0,0 +1,12 @@ +package com.shuwei.dish.match.model + +/** + * 物品名称批量查询入参 DTO + * 用于 queryGoodsInfoList 接口 goodsNames 字段的元素类型 + * @param goodsName 物品名称 + * @param rawMaterialsType 净材种类 + */ +data class GoodsNameQueryDTO( + val goodsName: String, + val rawMaterialsType: String +) diff --git a/app/src/main/java/com/shuwei/dish/match/model/SettingItem.kt b/app/src/main/java/com/shuwei/dish/match/model/SettingItem.kt new file mode 100644 index 0000000..16bc855 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/model/SettingItem.kt @@ -0,0 +1,40 @@ +package com.shuwei.dish.match.model + +/** + * 设置页面的列表项实体类 + * + * @param type 菜单项类型,用于唯一标识每一项 + * @param title 主标题文字 + * @param subtitle 副标题文字,仅部分项目(如菜品模式)有值 + * @param isHidden 是否为隐藏项,隐藏项需触发开发者模式后才显示 + * @param onClick 点击回调,初始化时直接注入对应的业务逻辑 + */ +data class SettingItem( + val type: Type, + val title: String, + val subtitle: String? = null, + val isHidden: Boolean = false, + val onClick: () -> Unit +) { + /** 设置菜单项类型枚举 */ + enum class Type { + /** 菜品模式切换 */ + COOK_MODE, + /** 调料区设置 */ + SEASONING_CONFIG, + /** 食材采集 */ + FOOD_COLLECT, + /** 减重配置 */ + WEIGHT_CONFIG, + /** 数据库查看(隐藏项) */ + DB_INSPECT, + /** 秤数据监控(隐藏项) */ + SCALE_OBSERVE, + /** 切换环境(隐藏项) */ + ENV_SWITCH, + /** 数据清除(隐藏项) */ + CLEAR_DATA, + /** 测试调料拿取(隐藏项) */ + TEST_SEASONING, + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/net/ApiService.kt b/app/src/main/java/com/shuwei/dish/match/net/ApiService.kt new file mode 100644 index 0000000..79bb69a --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/ApiService.kt @@ -0,0 +1,76 @@ +package com.shuwei.dish.match.net + +import com.shuwei.dish.match.base.GlobalData +import com.shuwei.dish.match.model.CookFoodDTO +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.model.GoodsItem +import okhttp3.MultipartBody +import okhttp3.RequestBody +import retrofit2.http.Body +import retrofit2.http.GET +import retrofit2.http.Multipart +import retrofit2.http.POST +import retrofit2.http.Part +import retrofit2.http.PartMap +import retrofit2.http.Query +import retrofit2.http.Url + +interface ApiService { + + /** + * 查询菜品详情 + */ + @GET + suspend fun getFoodDetail( + @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/getConstituteByFoodId", + @Query("foodId") foodId: String + ): ApiResponse + + + /** + * 提交制作菜品 + */ + @POST + suspend fun submitCookFood( + @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/saveConstitute", + @Body param: CookFoodDTO + ): ApiResponse + + /** + * 搜索菜品 + */ + @POST + suspend fun searchFoodList( + @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/queryPageList", + @Body param: MutableMap + ): ApiResponse?> + + /** + * 采样数据 + */ + @POST + suspend fun getSamplingList( + @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/queryHistoryGoodsInfoList", + @Body param: MutableMap + ): ApiResponse?> + + /** + * 物品信息(食材 goodsType=0,调料 goodsType=1 共用此接口) + */ + @POST + suspend fun queryGoodsList( + @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/queryGoodsInfoList", + @Body param: MutableMap + ): ApiResponse?> + + /** + * 提交采集图片数据 + */ + @Multipart + @POST + suspend fun uploadFoodVectorData( + @Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/matching/app/addWeighingCollectionData", + @PartMap params: MutableMap, + @Part foodPics: List + ): ApiResponse?> +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/net/ErrorType.kt b/app/src/main/java/com/shuwei/dish/match/net/ErrorType.kt new file mode 100644 index 0000000..b3c841d --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/ErrorType.kt @@ -0,0 +1,34 @@ +package com.shuwei.dish.match.net + +import java.io.IOException +import java.net.ConnectException +import java.net.SocketTimeoutException +import java.net.UnknownHostException + +// 1. 网络异常类型枚举 +enum class ErrorType { + NETWORK_ERROR, // 网络错误(无网) + TIMEOUT_ERROR, // 超时 + SERVER_ERROR, // 服务器错误(5xx) + CLIENT_ERROR, // 客户端错误(4xx) + PARSE_ERROR, // 数据解析错误 + UNKNOWN_ERROR // 未知错误 +} + +// 2. 自定义网络异常类 +class ApiException( + val errorType: ErrorType, + val errorCode: Int = -1, + val errorMsg: String, + val throwable: Throwable? = null +) : Exception(errorMsg, throwable) + +/** 将任意异常转换为 ApiException,供 safeApiCall 统一使用 */ +fun getApiException(e: Exception): ApiException = when (e) { + is ApiException -> e + is UnknownHostException -> ApiException(ErrorType.NETWORK_ERROR, errorMsg = "网络未连接,请检查网络") + is SocketTimeoutException -> ApiException(ErrorType.TIMEOUT_ERROR, errorMsg = "请求超时,请稍后重试") + is ConnectException -> ApiException(ErrorType.TIMEOUT_ERROR, errorMsg = "连接超时,请检查网络") + is IOException -> ApiException(ErrorType.NETWORK_ERROR, errorMsg = "网络异常:${e.message ?: "未知IO错误"}") + else -> ApiException(ErrorType.UNKNOWN_ERROR, errorMsg = "未知错误:${e.message ?: "未知"}", throwable = e) +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/net/ExceptionInterceptor.kt b/app/src/main/java/com/shuwei/dish/match/net/ExceptionInterceptor.kt new file mode 100644 index 0000000..5a80b92 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/ExceptionInterceptor.kt @@ -0,0 +1,40 @@ +//package com.shuwei.dish.match.net +// +//import android.annotation.SuppressLint +//import okhttp3.Interceptor +//import okhttp3.Response +//import org.apache.http.conn.ConnectTimeoutException +//import java.io.IOException +//import java.net.SocketTimeoutException +//import java.net.UnknownHostException +// +//class ExceptionInterceptor : Interceptor { +// @SuppressLint("SuspiciousIndentation") +// override fun intercept(chain: Interceptor.Chain): Response { +// val request = chain.request() +// try { +// val response = chain.proceed(request) +// // 拦截 HTTP 状态码异常(4xx/5xx) +// if (!response.isSuccessful) { +// val e = ApiException( +// errorType = if (response.code >= 500) ErrorType.SERVER_ERROR else ErrorType.CLIENT_ERROR, +// errorCode = response.code, +// errorMsg = "HTTP错误:${response.code}" +// ) +// throw e +// } +// return response +// } catch (e: Exception) { +// // 捕获各类网络异常并转换为自定义异常 +// val apiException = when (e) { +// is UnknownHostException -> ApiException(ErrorType.NETWORK_ERROR, errorMsg = "网络未连接,请检查网络") +// is SocketTimeoutException -> ApiException(ErrorType.TIMEOUT_ERROR, errorMsg = "请求超时,请稍后重试") +// is ConnectTimeoutException -> ApiException(ErrorType.TIMEOUT_ERROR, errorMsg = "连接超时,请检查网络") +// is IOException -> ApiException(ErrorType.NETWORK_ERROR, errorMsg = "网络异常:${e.message ?: "未知IO错误"}") +// is ApiException -> e // 已转换的异常直接抛出 +// else -> ApiException(ErrorType.UNKNOWN_ERROR, errorMsg = "未知错误:${e.message ?: "未知"}", throwable = e) +// } +// throw apiException +// } +// } +//} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/http/HttpClient.kt b/app/src/main/java/com/shuwei/dish/match/net/HttpManager.kt similarity index 50% rename from app/src/main/java/com/shuwei/dish/match/http/HttpClient.kt rename to app/src/main/java/com/shuwei/dish/match/net/HttpManager.kt index c46521d..edf89ee 100644 --- a/app/src/main/java/com/shuwei/dish/match/http/HttpClient.kt +++ b/app/src/main/java/com/shuwei/dish/match/net/HttpManager.kt @@ -1,20 +1,38 @@ -package com.shuwei.dish.match.http +package com.shuwei.dish.match.net import android.annotation.SuppressLint import android.util.Log -import okhttp3.Call -import okhttp3.Interceptor +import com.shuwei.dish.match.base.GlobalData import okhttp3.OkHttpClient -import okhttp3.Request -import okhttp3.Response +import okhttp3.logging.HttpLoggingInterceptor +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit import javax.net.ssl.* import java.security.SecureRandom import java.security.cert.X509Certificate +import kotlin.apply +import kotlin.jvm.java -class HttpClient private constructor() { - private val client: OkHttpClient by lazy { +val apiService: ApiService = Retrofit.Builder() + .baseUrl(GlobalData.appBaseUrl) +// .baseUrl(UrlConfig.DEVICE_BASE_URL) + .client(HttpManager.instance.client) + .addConverterFactory(GsonConverterFactory.create()) + .build() + .create(ApiService::class.java) + +//val apiService2: ApiService2 = Retrofit.Builder() +// .baseUrl(UrlConfig.BASE_URL) +//// .baseUrl(UrlConfig.DEVICE_BASE_URL) +// .client(HttpManager.instance.client) +// .addConverterFactory(GsonConverterFactory.create()) +// .build() +// .create(ApiService2::class.java) + +class HttpManager private constructor() { + val client: OkHttpClient by lazy { OkHttpClient.Builder() .apply { connectTimeout(15, TimeUnit.SECONDS) @@ -22,40 +40,32 @@ class HttpClient private constructor() { writeTimeout(15, TimeUnit.SECONDS) sslSocketFactory(createSSLSocketFactory(), TrustAllCerts()) hostnameVerifier { _, _ -> true } - //if (BuildConfig.Debug) { - addInterceptor(LoggingInterceptor()) - // } + addNetworkInterceptor(HttpLoggingInterceptor(logger = { + Log.d("HttpManager", "okhttp logger ==>${it}") + }).also { + it.level = HttpLoggingInterceptor.Level.BODY + }) + addInterceptor(RequestInterceptor()) +// addInterceptor(ExceptionInterceptor()) } .build() } companion object { - val instance by lazy { HttpClient() } - } - - fun newCall(request: Request): Call = client.newCall(request) - - inner class LoggingInterceptor : Interceptor { - override fun intercept(chain: Interceptor.Chain): Response { - val request = chain.request() - // 打印请求日志 - Log.d("OkHttp", "--> ${request.method} ${request.url}") - - val response = chain.proceed(request) - // 打印响应日志 - Log.d("OkHttp", "<-- ${response.code} ${response.request.url}") - - return response - } + val instance by lazy { HttpManager() } } // 信任所有证书的TrustManager实现 @SuppressLint("CustomX509TrustManager") class TrustAllCerts : X509TrustManager { @SuppressLint("TrustAllX509TrustManager") - override fun checkClientTrusted(chain: Array, authType: String) {} + override fun checkClientTrusted(chain: Array, authType: String) { + } + @SuppressLint("TrustAllX509TrustManager") - override fun checkServerTrusted(chain: Array, authType: String) {} + override fun checkServerTrusted(chain: Array, authType: String) { + } + override fun getAcceptedIssuers(): Array = arrayOf() } diff --git a/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt b/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt new file mode 100644 index 0000000..5dea168 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/NetViewModel.kt @@ -0,0 +1,258 @@ +package com.shuwei.dish.match.net + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.shuwei.dish.match.base.BaseApp +import com.shuwei.dish.match.model.CookFoodDTO +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.model.GoodsItem +import com.shuwei.dish.match.model.GoodsNameQueryDTO +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.launch +import okhttp3.MediaType.Companion.toMediaTypeOrNull +import okhttp3.MultipartBody +import okhttp3.RequestBody +import okhttp3.RequestBody.Companion.asRequestBody +import java.io.File + +/** + * 网络请求 ViewModel,所有网络操作通过 RemoteRepository 发起,不直接依赖 apiService + * @param repository 网络数据仓库,默认使用单例实例 + */ +class NetViewModel( + private val repository: RemoteRepository = RemoteRepository() +) : ViewModel() { + + /** + * 提交制作菜品的 UI 状态流,UI 层通过 collect 监听 + */ + private val _submitCookFoodState = MutableStateFlow>(UiState.Idle) + val submitCookFoodState: StateFlow> = _submitCookFoodState.asStateFlow() + + /** + * 提交制作菜品 + */ + fun submitCookFood(entity: CookFoodDTO) { + viewModelScope.launch { + _submitCookFoodState.value = UiState.Loading + _submitCookFoodState.value = repository.submitCookFood(entity) + } + } + + /** + * 查询菜品详情的 UI 状态流,UI 层通过 collect 监听 + */ + private val _foodDetailState = MutableStateFlow>(UiState.Idle) + val foodDetailState: StateFlow> = _foodDetailState.asStateFlow() + + /** + * 查询菜品详情 + */ + fun getFoodDetail(foodId: String) { + viewModelScope.launch { + _foodDetailState.value = UiState.Loading + _foodDetailState.value = repository.getFoodDetail(foodId) + } + } + + fun resetFoodDetailState() { + _foodDetailState.value = UiState.Idle + } + + /** + * 搜索菜品列表的 UI 状态流。 + * 使用 replay=1 保证 Fragment 重建后能收到最近一次结果; + * emit 时包一层 Event wrapper 打破 equals 去重,确保相同数据也能触发 collect。 + */ + private val _searchFoodState = MutableSharedFlow?>>( + replay = 1, + extraBufferCapacity = 1 + ) + val searchFoodState: SharedFlow?>> = _searchFoodState.asSharedFlow() + + /** + * 搜索菜品列表 + */ + fun searchFoodList(param: MutableMap) { + viewModelScope.launch { + // pageNum=1 时先 emit Idle 重置 replay 缓存,防止相同数据导致 SharedFlow 去重不触发 + if (param["pageNum"] == 1) { + _searchFoodState.emit(UiState.Idle) + } + _searchFoodState.emit(UiState.Loading) + _searchFoodState.emit(repository.searchFoodList(param)) + } + } + + /** + * 搜索菜品列表(回调版本),结果通过 onResult 直接回调,不经过 StateFlow/SharedFlow + * 适合需要每次都能触发的场景(下拉刷新、上拉加载更多) + */ + fun searchFoodListWithCallback( + param: MutableMap, + onLoading: () -> Unit = {}, + onResult: (UiState?>) -> Unit + ) { + viewModelScope.launch { + onLoading() + onResult(repository.searchFoodList(param)) + } + } + + /** + */ + private val _samplingListState = MutableStateFlow?>>(UiState.Idle) + val samplingListState: StateFlow?>> = _samplingListState.asStateFlow() + fun resetSamplingListState() { + _samplingListState.value = UiState.Idle + } + + /** + * 查询采样数据列表 + */ + fun getSamplingList(param: MutableMap) { + viewModelScope.launch { + _samplingListState.value = UiState.Loading + _samplingListState.value = repository.getSamplingList(param) + } + } + + /** + * 查询采样数据列表(回调版本),结果通过 onResult 直接回调,不经过 StateFlow + * 适合不需要跨生命周期持续监听的一次性查询场景 + */ + fun getSamplingListWithCallback( + param: MutableMap, + onLoading: () -> Unit = {}, + onResult: (UiState?>) -> Unit + ) { + viewModelScope.launch { + onLoading() + onResult(repository.getSamplingList(param)) + } + } + + /** + * 查询物品信息列表的 UI 状态流(食材/调料共用),UI 层通过 collect 监听 + */ + private val _goodsListState = MutableStateFlow?>>(UiState.Idle) + val goodsListState: StateFlow?>> = _goodsListState.asStateFlow() + + /** + * 食材搜索弹窗专用的 UI 状态流,与 goodsListState 隔离,避免弹窗请求影响其他页面 + */ + private val _foodSearchGoodsListState = MutableStateFlow?>>(UiState.Idle) + val foodSearchGoodsListState: StateFlow?>> = _foodSearchGoodsListState.asStateFlow() + + fun resetGoodsListState() { + _goodsListState.value = UiState.Idle + } + + /** + * 查询物品信息列表(食材 goodsType=0,调料 goodsType=1 共用) + * @param goodsType 物品类型:0=食材,1=调料 + * @param placeId 场所 ID,默认取当前食堂 ID + * @param pageNum 页码,默认第 1 页 + * @param pageSize 每页条数,默认 50 + * @param goodsName 按名称模糊搜索,为 null 时不传该字段 + * @param goodsNames 按名称+净材种类列表批量查询,为 null 时不传该字段 + */ + fun queryGoodsList( + goodsType: String, + placeId: String = BaseApp.canteenId, + pageNum: Int = 1, + pageSize: Int = 50, + goodsName: String? = null, + goodsNames: List? = null + ) { + val param = mutableMapOf( + "goodsType" to goodsType, + "placeId" to placeId, + "pageNum" to pageNum, + "pageSize" to pageSize + ) + goodsName?.let { param["goodsName"] = it } + goodsNames?.let { param["goodsNames"] = it } + viewModelScope.launch { + _goodsListState.value = UiState.Loading + _goodsListState.value = repository.queryGoodsList(param) + } + } + + /** + * 食材搜索弹窗专用的查询方法,结果写入 foodSearchGoodsListState,不影响 goodsListState + * @param goodsType 物品类型:0=食材,1=调料 + * @param placeId 场所 ID,默认取当前食堂 ID + * @param pageNum 页码,默认第 1 页 + * @param pageSize 每页条数,默认 50 + * @param goodsName 按名称模糊搜索,为 null 时不传该字段 + * @param goodsNames 按名称+净材种类列表批量查询,为 null 时不传该字段 + */ + fun queryFoodSearchGoodsList( + goodsType: String, + placeId: String = BaseApp.canteenId, + pageNum: Int = 1, + pageSize: Int = 50, + goodsName: String? = null, + goodsNames: List? = null + ) { + val param = mutableMapOf( + "goodsType" to goodsType, + "placeId" to placeId, + "pageNum" to pageNum, + "pageSize" to pageSize + ) + goodsName?.let { param["goodsName"] = it } + goodsNames?.let { param["goodsNames"] = it } + viewModelScope.launch { + _foodSearchGoodsListState.value = UiState.Loading + _foodSearchGoodsListState.value = repository.queryGoodsList(param) + } + } + + /** + * 上传采集菜品信息 UI 状态流,UI 层通过 collect 监听 + */ + private val _uploadCollectState = MutableStateFlow?>>(UiState.Idle) + val uploadCollectState: StateFlow?>> = _uploadCollectState.asStateFlow() + + /** + * 上传采集菜品信息(触发 StateFlow,适合单次上传场景) + */ + fun uploadFoodVectorData( + fileList: List, + params: MutableMap + ) { + val fileParts = fileList.map { file -> + file.asRequestBody("multipart/form-data".toMediaTypeOrNull()) + .let { MultipartBody.Part.createFormData("foodPics", file.name, it) } + } + viewModelScope.launch { + _uploadCollectState.value = UiState.Loading + _uploadCollectState.value = repository.uploadFoodVectorData(params, fileParts) + } + } + + /** + * 上传采集菜品信息(直接返回结果,适合分批循环上传场景) + * @param fileList 图片文件列表 + * @param params 附加参数 + * @return UiState 包装的结果 + */ + suspend fun uploadFoodVectorDataBatch( + fileList: List, + params: MutableMap + ): UiState?> { + val fileParts = fileList.map { file -> + //multipart/form-data image/jpeg + file.asRequestBody("multipart/form-data".toMediaTypeOrNull()) + .let { MultipartBody.Part.createFormData("foodPics", file.name, it) } + } + return repository.uploadFoodVectorData(params, fileParts) + } +} diff --git a/app/src/main/java/com/shuwei/dish/match/net/RemoteRepository.kt b/app/src/main/java/com/shuwei/dish/match/net/RemoteRepository.kt new file mode 100644 index 0000000..25d6026 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/RemoteRepository.kt @@ -0,0 +1,53 @@ +package com.shuwei.dish.match.net + +import com.shuwei.dish.match.model.CookFoodDTO +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.model.GoodsItem +import okhttp3.MultipartBody +import okhttp3.RequestBody + +/** + * 网络数据仓库,封装所有 ApiService 调用 + * ViewModel 不直接接触 apiService,统一通过此类发起网络请求 + */ +class RemoteRepository { + + /** 统一封装 API 调用:处理响应映射和异常转换,返回 UiState */ + private suspend fun safeApiCall(call: suspend () -> ApiResponse): UiState { + return try { + val resp = call() + if (resp.isSuccess()) UiState.Success(resp.data) + else UiState.Error(resp.code, resp.msg ?: "") + } catch (e: Exception) { + UiState.Error("-1", getApiException(e).errorMsg) + } + } + + /** 提交制作菜品 */ + suspend fun submitCookFood(entity: CookFoodDTO): UiState = + safeApiCall { apiService.submitCookFood(param = entity) } + + /** 查询菜品详情 */ + suspend fun getFoodDetail(foodId: String): UiState = + safeApiCall { apiService.getFoodDetail(foodId = foodId) } + + /** 搜索菜品列表 */ + suspend fun searchFoodList(param: MutableMap): UiState?> = + safeApiCall { apiService.searchFoodList(param = param) } + + /** 查询采样数据列表 */ + suspend fun getSamplingList(param: MutableMap): UiState?> = + safeApiCall { apiService.getSamplingList(param = param) } + + /** 查询物品信息列表(食材 goodsType=0,调料 goodsType=1 共用) */ + suspend fun queryGoodsList(param: MutableMap): UiState?> = + safeApiCall { apiService.queryGoodsList(param = param) } + + /** 上传采集数据 */ + suspend fun uploadFoodVectorData( + params: MutableMap, + foodPics: List + ): UiState?> = + safeApiCall { apiService.uploadFoodVectorData(params = params, foodPics = foodPics) } + +} diff --git a/app/src/main/java/com/shuwei/dish/match/net/RequestInterceptor.kt b/app/src/main/java/com/shuwei/dish/match/net/RequestInterceptor.kt new file mode 100644 index 0000000..45b0edb --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/RequestInterceptor.kt @@ -0,0 +1,22 @@ +package com.shuwei.dish.match.net + +import com.shuwei.dish.match.base.GlobalData +import okhttp3.Interceptor +import okhttp3.Response + +/** + * 请求拦截器 + */ +class RequestInterceptor : Interceptor { + override fun intercept(chain: Interceptor.Chain): Response { + val originalRequest = chain.request() + val requestBuilder = originalRequest.newBuilder() + .header("X-Access-Token", "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJjYW50ZWVuSWQiOiJiZTE1NDgzMS0zNDY2LTNiYTItYTJlYS01NzY1MmM5MTlmZWQiLCJ0eXBlIjoiNCIsInVzZXJJZCI6IjAwMDAwMDAwMDAwMDAwMDAwMDEifQ.sN40cOC-O5WQFrF4IDUs8fFlkNdUKLbJt_rHyTsgYYM") + .header("X-DEVICE-CODE", GlobalData.deviceId) + .header("authorization", "57ee87183f2a4fa59683ec9ef41c8f5d") + + val newRequest = requestBuilder.build() + + return chain.proceed(newRequest) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/shuwei/dish/match/net/RespData.kt b/app/src/main/java/com/shuwei/dish/match/net/RespData.kt new file mode 100644 index 0000000..9e7e670 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/RespData.kt @@ -0,0 +1,9 @@ +package com.shuwei.dish.match.net + +data class ApiResponse( + val code: String, + val msg: String? = "", + val data: T? = null +) { + fun isSuccess() = code == "00000" +} diff --git a/app/src/main/java/com/shuwei/dish/match/net/UiState.kt b/app/src/main/java/com/shuwei/dish/match/net/UiState.kt new file mode 100644 index 0000000..3374de9 --- /dev/null +++ b/app/src/main/java/com/shuwei/dish/match/net/UiState.kt @@ -0,0 +1,15 @@ +package com.shuwei.dish.match.net + +/** + * 统一 UI 状态模型,用于 ViewModel 向 UI 层传递请求状态 + * Idle — 初始状态,未发起请求 + * Loading — 请求进行中 + * Success — 请求成功,携带数据 + * Error — 请求失败,携带错误码和错误信息 + */ +sealed class UiState { + object Idle : UiState() + object Loading : UiState() + data class Success(val data: T) : UiState() + data class Error(val code: String, val msg: String) : UiState() +} diff --git a/app/src/main/java/com/shuwei/dish/match/ui/DeviceConfigActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/DeviceConfigActivity.kt index 59d6ce4..c03e6aa 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/DeviceConfigActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/DeviceConfigActivity.kt @@ -14,14 +14,13 @@ import android.widget.FrameLayout import android.widget.TextView import androidx.core.graphics.toColorInt import androidx.core.view.forEach -import androidx.lifecycle.ViewModelProvider import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding -import com.shuwei.dish.match.db.AppRepository + import com.shuwei.dish.match.dialog.CommonDialog -import com.shuwei.dish.match.entity.SeasoningEntity -import com.shuwei.dish.match.entity.ResetReasoningRecord +import com.shuwei.dish.match.db.entity.SeasoningEntity +import com.shuwei.dish.match.db.entity.ResetReasoningRecord +import com.shuwei.dish.match.model.GoodsItem import com.shuwei.dish.match.utils.AddressUtil import com.shuwei.dish.match.utils.JsonAssetsLoader import com.shuwei.dish.match.utils.MultiClickDetector @@ -32,8 +31,6 @@ import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.TextCellAdapter import com.shuwei.dish.match.databinding.ActivityDeviceConfigBinding @@ -64,8 +61,6 @@ class DeviceConfigActivity : BaseActivity() { private lateinit var binding: ActivityDeviceConfigBinding - private lateinit var appViewModel: AppViewModel - private var cookMode: Int = 0 // private val weightArray = SparseIntArray() private val weightArray = SparseArray() @@ -76,7 +71,6 @@ class DeviceConfigActivity : BaseActivity() { binding = ActivityDeviceConfigBinding.inflate(layoutInflater) setContentView(binding.root) cookMode = intent.getIntExtra(COOK_MODE, 0) - initViewModel() setHeaderBackground() setTitleBar(titleBarAction = { it.visible() @@ -205,14 +199,6 @@ class DeviceConfigActivity : BaseActivity() { }.onFailure { it.printStackTrace() } } - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = - AppFactory(AppRepository(db.appDao())) - appViewModel = - ViewModelProvider(this, factory)[AppViewModel::class.java] - } - private fun loadSeasoning() { appViewModel.loadSeasoning { seasoningItems.clear() @@ -268,7 +254,7 @@ class DeviceConfigActivity : BaseActivity() { } } - private fun syncItem(oldItem: SeasoningEntity, newItem: SeasoningEntity) { + private fun syncItem(oldItem: SeasoningEntity, newItem: GoodsItem) { oldItem.run { goodsId = newItem.goodsId goodsName = newItem.goodsName @@ -278,11 +264,6 @@ class DeviceConfigActivity : BaseActivity() { allEdible = newItem.allEdible useWeight = newItem.useWeight popularName = newItem.popularName - canteenId = newItem.canteenId - relateionType_dictText = newItem.relateionType_dictText - - foodId = newItem.foodId - sort = newItem.sort } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt index e1bb9c7..97de239 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/DishSamplingActivity.kt @@ -5,20 +5,18 @@ import android.content.Intent import android.os.Bundle import android.util.Log import android.view.View -import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.DishPartAdapter import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivityDishSamplingBinding import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding -import com.shuwei.dish.match.db.AppRepository + import com.shuwei.dish.match.dialog.BottomDialog import com.shuwei.dish.match.dialog.CommonDialog -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.db.entity.CookFoodEntity +import com.shuwei.dish.match.model.GoodsItem +import com.shuwei.dish.match.model.FoodRecord import com.shuwei.dish.match.utils.AddressUtil import com.shuwei.dish.match.utils.KeyboardUtil import com.shuwei.dish.match.utils.WeightUtil @@ -29,8 +27,6 @@ import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.toJsonString import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory import java.io.Serializable class DishSamplingActivity : BaseActivity() { @@ -58,7 +54,6 @@ class DishSamplingActivity : BaseActivity() { setTitleBar() setHeaderBackground() addViewClickListener() - initViewModel() foodName = intent.getStringExtra(FOOD_NAME) binding.etInputDish.setText(foodName) @@ -92,16 +87,6 @@ class DishSamplingActivity : BaseActivity() { }) } - private lateinit var appViewModel: AppViewModel - - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = - AppFactory(AppRepository(db.appDao())) - appViewModel = - ViewModelProvider(this, factory)[AppViewModel::class.java] - } - fun getCookFoodList(action: (MutableList?) -> Unit) { appViewModel.getCookFoodList(cookMode = 1) { action(it) } } @@ -142,9 +127,9 @@ class DishSamplingActivity : BaseActivity() { } - val list: MutableList = mutableListOf() + val list: MutableList = mutableListOf() - private var tempDishEntity: CookFoodGoodsEntity? = null + private var tempDishEntity: GoodsItem? = null private var dishMaterialType = 1 @@ -236,7 +221,7 @@ class DishSamplingActivity : BaseActivity() { Log.d(TAG, "goToSubmit: goodsList:${list.toJsonString()}") // startActivity { startActivity { - putExtra(SubmitFoodActivity.GOODS_LIST, list as java.io.Serializable) + putExtra(SubmitFoodActivity.GOODS_LIST, list.map { it.toCookFoodGoodsEntity() }.toMutableList() as java.io.Serializable) putExtra(SubmitFoodActivity.FOOD_ITEM, food as Serializable) } } @@ -276,16 +261,7 @@ class DishSamplingActivity : BaseActivity() { setText(item.goodsName) setSelection(length()) } - tempDishEntity = CookFoodGoodsEntity().apply { - goodsId = item.goodsId - goodsName = item.goodsName - - relateionType = item.relateionType ?: 0 - goodsOrRelationCode = item.goodsOrRelationCode - - // TODO: 待定 - //allEdible = item.allEdible - } + tempDishEntity = item } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt index ee772d0..6aa4bf7 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/FoodSearchActivity.kt @@ -12,16 +12,12 @@ import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivityFoodSearchBinding import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding -import com.shuwei.dish.match.entity.FoodRecord -import com.shuwei.dish.match.entity.FoodRecordBean -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.KeyboardUtil import com.shuwei.dish.match.utils.ext.addOnActionSearchListener import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.startActivity -import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible import java.io.Serializable @@ -146,58 +142,51 @@ class FoodSearchActivity : BaseActivity() { @SuppressLint("NotifyDataSetChanged") private fun queryListInfo(input: String) { showLoading() - val map = mutableMapOf( + val param = mutableMapOf( "foodName" to input.trim(), - "pageNo" to "$pageNo", - "pageSize" to "$pageSize", + "pageNo" to pageNo, + "pageSize" to pageSize, "canteenId" to BaseApp.canteenId ) if (dinnerType.toInt() > 0) { - map["dinnerType"] = getDinnerTypeText() + param["dinnerType"] = getDinnerTypeText() } - val sb = StringBuilder(UrlConfig.QUERY_FOOD_LIST).apply { - append("?") - map.forEach { (key, value) -> append("$key=$value&") } - } - sb.deleteCharAt(sb.length - 1) - HttpUtil.get( - url = sb.toString(), - doSuccess = { - delayDismissLoading() - finishRefresh() - binding.refreshLayout.run { - setEnableRefresh(true) - } - val json = it.toJsonString() - val recordBean: FoodRecordBean? = json.toObject() - if (recordBean == null || recordBean.records.isNullOrEmpty()) { -// toast("暂未搜索到相关菜品信息") - if (pageNo == 1) { - loadEmptyView() + netViewModel.searchFoodListWithCallback( + param = param, + onResult = { state -> + when (state) { + is UiState.Success -> { + delayDismissLoading() + finishRefresh() + binding.refreshLayout.setEnableRefresh(true) + val records = state.data + if (records.isNullOrEmpty()) { + if (pageNo == 1) { + loadEmptyView() + } + return@searchFoodListWithCallback + } + if (pageNo == 1) { + list.clear() + } + list.addAll(records) + recordAdapter.notifyDataSetChanged() + val isLoadMoreEnable = records.size >= pageSize + binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) + if (isLoadMoreEnable) { + pageNo++ + } } - return@get - } - val records = recordBean.records - if (pageNo == 1) { - list.clear() - } - list.addAll(records!!) - recordAdapter.notifyDataSetChanged() - val isLoadMoreEnable = records.size >= pageSize - binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) - if (isLoadMoreEnable) { - pageNo++ - } - }, - doFailure = { code, msg -> - binding.refreshLayout.run { - setEnableRefresh(true) - } - toast(msg) - delayDismissLoading() - finishRefresh() - if (pageNo == 1) { - loadEmptyView() + is UiState.Error -> { + binding.refreshLayout.setEnableRefresh(true) + toast(state.msg) + delayDismissLoading() + finishRefresh() + if (pageNo == 1) { + loadEmptyView() + } + } + else -> {} } } ) diff --git a/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt index baaa484..76a87b3 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/HomeActivity.kt @@ -2,22 +2,18 @@ package com.shuwei.dish.match.ui import android.annotation.SuppressLint import android.os.Bundle -import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.HomeModeAdapter import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivityHomeBinding -import com.shuwei.dish.match.db.AppRepository -import com.shuwei.dish.match.entity.HomeModeBean -import com.shuwei.dish.match.http.HttpUtil + +import com.shuwei.dish.match.model.HomeModeBean import com.shuwei.dish.match.utils.SpTool import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.toast -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory class HomeActivity : BaseActivity() { @@ -33,7 +29,6 @@ class HomeActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) statusBarDarkFont(enable = true) - initViewModel() // // 获取 ANDROID_ID // val deviceId = // Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID) @@ -43,7 +38,7 @@ class HomeActivity : BaseActivity() { binding = ActivityHomeBinding.inflate(layoutInflater) setContentView(binding.root) setHeaderBackground(isHomePage = true) - binding.radioGroup.setOnCheckedChangeListener { group, checkedId -> + binding.radioGroup.setOnCheckedChangeListener { _, checkedId -> if (checkedId == R.id.radioSw) { BaseApp.canteenId = "1678234139391512577" } else { @@ -134,14 +129,6 @@ class HomeActivity : BaseActivity() { ) } - private lateinit var appViewModel: AppViewModel - - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = AppFactory(AppRepository(db.appDao())) - appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java] - } - private fun goSampling() { appViewModel.loadSeasoning { if (it.isEmpty()) { diff --git a/app/src/main/java/com/shuwei/dish/match/ui/InitActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/InitActivity.kt index 6e21d48..6c7fd99 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/InitActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/InitActivity.kt @@ -4,27 +4,18 @@ import android.annotation.SuppressLint import android.os.Bundle import android.provider.Settings import android.util.Log -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.lifecycleScope import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivityInitBinding -import com.shuwei.dish.match.db.AppRepository -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.base.GlobalData import com.shuwei.dish.match.utils.AppUtil -import com.shuwei.dish.match.utils.QRCodeUtil import com.shuwei.dish.match.utils.SpTool import com.shuwei.dish.match.utils.WeightUtil -import com.shuwei.dish.match.utils.ext.dp import com.shuwei.dish.match.utils.ext.invisible import com.shuwei.dish.match.utils.ext.startActivity -import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flow import kotlinx.coroutines.launch @@ -36,8 +27,6 @@ class InitActivity : BaseActivity() { } private lateinit var binding: ActivityInitBinding - private lateinit var appViewModel: AppViewModel - @SuppressLint("HardwareIds") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -56,7 +45,7 @@ class InitActivity : BaseActivity() { androidId = "39a7abdd06b3c7ab" BaseApp.deviceId = androidId SpTool.put(SpTool.DEVICE_ID, androidId) - BaseApp.configUrl = UrlConfig.BASE_URL + GlobalData.appBaseUrl = GlobalData.TEST_BASE_URL BaseApp.canteenId = "0" // // TODO: 以上保存deviceId用于临时使用,后续改为下面注释方式 @@ -82,22 +71,12 @@ class InitActivity : BaseActivity() { binding.ivQrCode.invisible() binding.btnInit.invisible() - initViewModel() WeightUtil.init() WeightUtil.getWeight() WeightUtil.startContinuousRead() - HttpUtil.getAppToken() - - HttpUtil.loopGetToken = true countDown() } - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = AppFactory(AppRepository(db.appDao())) - appViewModel = ViewModelProvider(this, factory)[AppViewModel::class.java] - } - private fun startNextPage() { val launchPageType = SpTool.getInt(SpTool.LAUNCH_PAGE_TYPE, -1) when (launchPageType) { @@ -157,68 +136,6 @@ class InitActivity : BaseActivity() { } } - private fun initConfig() { - binding.ivQrCode.setImageBitmap( - QRCodeUtil.generateQRCode( - content = BaseApp.deviceId?:"", - size = 200.dp - ) - ) - binding.btnInit.setOnClickListener { - HttpUtil.loopGetToken = false - getDeviceConfig() - } - } - - private fun getDeviceConfig() { - val tokenUrl = "${UrlConfig.DEVICE_TOKEN}?qrcodeId=${BaseApp.deviceId}&appVersion=${BaseApp.appVersion}" - HttpUtil.get(url = tokenUrl, doSuccess = { token -> - Log.d(TAG, "initConfig: $token") - getConfig(token.toString()) - }, doFailure = { code, msg -> - Log.d(TAG, "initConfig: $code,$msg") - }) - } - private fun getConfig(token: String) { - val deviceConfigUrl = "${UrlConfig.DEVICE_CONFIG}?equipmentCode=${BaseApp.deviceId}&&appVersion=${BaseApp.appVersion}" - HttpUtil.get(url = deviceConfigUrl, header = mutableMapOf( - "X-Access-Token" to token - ), doSuccess = { - Log.d(TAG, "getDeviceConfig: $it") - val data = it.toJsonString() - val checkResult = checkConfigData(data) - if (checkResult.not()) { - toast("初始化设备失败,请稍后重试") - return@get - } - HttpUtil.loopGetToken = true - SpTool.put(SpTool.DEVICE_CONFIG_CACHE, data) - startActivity() -// finish() - }, doFailure = { code, msg -> - Log.d(TAG, "getDeviceConfig: $code,$msg") - toast("初始化设备失败,请稍后重试,code=${code},msg=${msg}") - }) - } - - data class DeviceConfigBean( - var appPackageUrl:String? = null, - var canteenId:String? = null - ) - - private fun checkConfigData(data: String): Boolean { - if (data.isBlank()) { - return false - } - val config = data.toObject() - if (config == null) { - return false - } - BaseApp.configUrl = config.appPackageUrl?:"" - BaseApp.canteenId = config.canteenId?:"" - return true - } - override fun onDestroy() { WeightUtil.stopContinuousRead() super.onDestroy() diff --git a/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt index 19b02ca..3f7c855 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/PrepareCookActivity.kt @@ -14,11 +14,15 @@ import com.shuwei.dish.match.databinding.ActivityPrepareCookBinding import com.shuwei.dish.match.databinding.LayoutFoodRemindBinding import com.shuwei.dish.match.dialog.BottomDialog import com.shuwei.dish.match.dialog.CommonDialog -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.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.model.GoodsItem +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.model.CookFoodDTO +import com.shuwei.dish.match.model.FoodRecord +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.shuwei.dish.match.net.UiState +import kotlinx.coroutines.launch import com.shuwei.dish.match.utils.AddressUtil import com.shuwei.dish.match.utils.SwipeCallback import com.shuwei.dish.match.utils.WeightUtil @@ -26,7 +30,6 @@ import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible import java.io.Serializable @@ -44,8 +47,6 @@ class PrepareCookActivity : BaseActivity() { private var food: FoodRecord? = null private var goodsList: MutableList? = null - - @Suppress("DEPRECATION") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityPrepareCookBinding.inflate(layoutInflater) @@ -69,6 +70,7 @@ class PrepareCookActivity : BaseActivity() { food = intent.extras?.getSerializable(FOOD_ITEM) as FoodRecord? binding.tvDishName.text = food?.foodName ?: "" addViewClickListener() + initObserver() getDishDetail() } @@ -115,12 +117,7 @@ class PrepareCookActivity : BaseActivity() { toast("不允许重复添加同一食材") return@show } - list.add(CookFoodGoodsEntity().apply { - goodsId = item.goodsId - goodsName = item.goodsName -// materialType = - isNewDishType = true - }) + list.add(GoodsItem(goodsId = item.goodsId, goodsName = item.goodsName, isNewDishType = true)) onItemClick(list.size - 1) } } @@ -163,15 +160,8 @@ class PrepareCookActivity : BaseActivity() { } list.forEach { if (it.isSetFinished) { - goodsList?.add(CookFoodGoodsEntity().apply { - foodId = food!!.foodId - goodsId = it.goodsId - relateionType = it.relateionType - allEdible = it.allEdible - goodsName = it.goodsName - materialType = it.materialType - useWeight = it.useWeight ?: 0.toDouble() - goodsOrRelationCode = it.goodsOrRelationCode + goodsList?.add(it.toCookFoodGoodsEntity().also { entity -> + entity.foodId = food!!.foodId }) } } @@ -193,35 +183,45 @@ class PrepareCookActivity : BaseActivity() { } - private fun getDishDetail() { - //649 - val url = "${UrlConfig.DISH_DETAIL}?foodId=${food?.foodId}" - HttpUtil.get( - url = url, - doSuccess = { - val json = it.toJsonString() - Log.d(TAG, "getDishDetail: json:$json") - val detail: CookFoodEntity? = json.toObject() - if (detail == null) { - toast("查询菜品信息为空") - return@get + private fun initObserver() { + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + netViewModel.foodDetailState.collect { state -> + when (state) { + is UiState.Loading -> showLoading() + is UiState.Success -> { + dismissLoading() + netViewModel.resetFoodDetailState() + val detail = state.data + if (detail == null) { + toast("查询菜品信息为空") + return@collect + } + loadDishDetail(detail) + } + is UiState.Error -> { + dismissLoading() + toast(state.msg) + } + else -> {} + } } - loadDishDetail(detail) - }) { code, msg -> - toast(msg) + } } } - private fun loadDishDetail(detail: CookFoodEntity) { - val voList = detail.stFoodInfoConstituteList + private fun getDishDetail() { + netViewModel.getFoodDetail(food?.foodId ?: "") + } + + private fun loadDishDetail(detail: CookFoodDTO) { + val voList = detail.matchingConstituteInfoList if (voList.isNullOrEmpty()) { return } //筛选出主材和辅材 val tempDate = voList.filter { it.materialType == 1 || it.materialType == 2 } - .apply { - forEach { it.useWeight = 0.toDouble() } - } + .map { it.toGoodsItem().also { item -> item.useWeight = 0.0 } } list.clear() list.addAll(tempDate) firstReqSize = list.size @@ -229,7 +229,7 @@ class PrepareCookActivity : BaseActivity() { } private var firstReqSize = 0 - private val list = mutableListOf() + private val list = mutableListOf() private val dishPartAdapter by lazy { DishPartAdapter(list).apply { setOnItemClickListener { _, _, positon -> @@ -247,8 +247,8 @@ class PrepareCookActivity : BaseActivity() { } binding.tvDishType.text = if (it.materialType == 1) "主材" else if (it.materialType == 2) "辅材" else "" - list.forEach { bean -> bean.isItemClicked = false } - it.isItemClicked = true + list.forEach { bean -> bean.isClicked = false } + it.isClicked = true dishPartAdapter.notifyDataSetChanged() if (it.isNewDishType) { diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt index e14c13c..5175715 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SamplingListActivity.kt @@ -6,7 +6,6 @@ import android.os.Bundle import android.util.Log import android.view.LayoutInflater import android.view.MotionEvent -import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.shuwei.dish.match.R @@ -15,23 +14,17 @@ import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivitySamplingBinding import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding -import com.shuwei.dish.match.db.AppRepository + import com.shuwei.dish.match.dialog.DialogTool -import com.shuwei.dish.match.entity.CookFoodEntity -import com.shuwei.dish.match.entity.FoodRecord -import com.shuwei.dish.match.entity.FoodRecordBean -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.DateTimeUtil import com.shuwei.dish.match.utils.SwipeCallback import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.startActivity -import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory import java.io.Serializable class SamplingListActivity : BaseActivity() { @@ -49,7 +42,6 @@ class SamplingListActivity : BaseActivity() { setContentView(binding.root) setTitleBar() setHeaderBackground() - initViewModel() initRecyclerView() addViewListener() requestData(true) @@ -76,16 +68,6 @@ class SamplingListActivity : BaseActivity() { } - private lateinit var appViewModel: AppViewModel - - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = - AppFactory(AppRepository(db.appDao())) - appViewModel = - ViewModelProvider(this, factory)[AppViewModel::class.java] - } - fun getCookFoodList(action: (MutableList?) -> Unit) { appViewModel.getCookFoodList(cookMode = 1) { action(it) } } @@ -204,33 +186,29 @@ class SamplingListActivity : BaseActivity() { @SuppressLint("NotifyDataSetChanged") fun getSamplingList() { showLoading() - val map = mapOf( -// "foodName" to input, - "pageNo" to "$pageNo", - "pageSize" to "$pageSize", + val param = mutableMapOf( + "pageNo" to pageNo, + "pageSize" to pageSize, "canteenId" to BaseApp.canteenId ) - val sb = StringBuilder(UrlConfig.SAMPLING_LIST).apply { - append("?") - map.forEach { (key, value) -> append("$key=$value&") } - } - sb.deleteCharAt(sb.length - 1) - HttpUtil.get( - url = sb.toString(), - doSuccess = { - loadDishList(it) - }, doFailure = { code, msg -> - binding.refreshLayout.run { - setEnableRefresh(true) + netViewModel.getSamplingListWithCallback( + param = param, + onResult = { state -> + when (state) { + is UiState.Success -> loadDishList(state.data) + is UiState.Error -> { + binding.refreshLayout.setEnableRefresh(true) + toast(state.msg) + finishRefresh() + delayDismissLoading() + if (pageNo == 1) { + loadEmptyView() + } + } + else -> {} } - toast(msg) - finishRefresh() - binding.refreshLayout.setEnableRefresh(true) - delayDismissLoading() - if (pageNo == 1) { - loadEmptyView() - } - }) + } + ) } private fun finishRefresh() { @@ -332,26 +310,20 @@ class SamplingListActivity : BaseActivity() { } @SuppressLint("NotifyDataSetChanged") - private fun loadDishList(data: Any) { - binding.refreshLayout.run { - setEnableRefresh(true) - } + private fun loadDishList(records: MutableList?) { + binding.refreshLayout.setEnableRefresh(true) delayDismissLoading() finishRefresh() - val json = data.toJsonString() - val recordBean: FoodRecordBean? = json.toObject() - if (recordBean == null || recordBean.records.isNullOrEmpty()) { - //toast("暂未搜索到相关菜品信息") + if (records.isNullOrEmpty()) { if (pageNo == 1) { loadEmptyView() } return } - val records = recordBean.records if (pageNo == 1) { list.clear() } - list.addAll(records!!) + list.addAll(records) dishAdapter.notifyDataSetChanged() val isLoadMoreEnable = records.size >= pageSize binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt index c6d521d..cb59498 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SelectDishActivity.kt @@ -5,14 +5,12 @@ import android.app.Dialog import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider import com.shuwei.dish.match.R import com.shuwei.dish.match.base.BaseActivity -import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivitySelectDishBinding -import com.shuwei.dish.match.db.AppRepository + import com.shuwei.dish.match.dialog.DialogTool -import com.shuwei.dish.match.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodEntity import com.shuwei.dish.match.ui.fragment.DishListFragment import com.shuwei.dish.match.utils.KeyboardUtil import com.shuwei.dish.match.utils.ext.addOnActionSearchListener @@ -20,8 +18,6 @@ import com.shuwei.dish.match.utils.ext.gone import com.shuwei.dish.match.utils.ext.startActivity import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory class SelectDishActivity : BaseActivity() { @@ -32,8 +28,6 @@ class SelectDishActivity : BaseActivity() { private lateinit var binding: ActivitySelectDishBinding - private lateinit var appViewModel: AppViewModel - private var dinnerType: String = "1" private val fragmentList = mutableListOf().apply { @@ -62,19 +56,10 @@ class SelectDishActivity : BaseActivity() { }, backAction = { it.gone() }) - initViewModel() addViewListener() loadFragment() } - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = - AppFactory(AppRepository(db.appDao())) - appViewModel = - ViewModelProvider(this, factory)[AppViewModel::class.java] - } - fun getCookFoodList(action: (MutableList?) -> Unit) { appViewModel.getCookFoodList(cookMode = 0, dinnerType = dinnerType) { action(it) } } diff --git a/app/src/main/java/com/shuwei/dish/match/ui/SubmitFoodActivity.kt b/app/src/main/java/com/shuwei/dish/match/ui/SubmitFoodActivity.kt index 280e3c0..2ef4a76 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/SubmitFoodActivity.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/SubmitFoodActivity.kt @@ -8,22 +8,25 @@ import android.util.SparseIntArray import android.widget.FrameLayout import android.widget.TextView import androidx.core.util.forEach -import androidx.lifecycle.ViewModelProvider import com.google.gson.reflect.TypeToken import com.shuwei.dish.match.R import com.shuwei.dish.match.adapter.TextCellAdapter import com.shuwei.dish.match.base.BaseActivity import com.shuwei.dish.match.base.BaseApp import com.shuwei.dish.match.databinding.ActivitySubmitFoodBinding -import com.shuwei.dish.match.db.AppRepository -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 -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig + +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.db.entity.SeasoningEntity +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import com.shuwei.dish.match.model.toDTO +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.utils.AddressUtil +import kotlinx.coroutines.launch import com.shuwei.dish.match.utils.WeightUtil import com.shuwei.dish.match.utils.ext.clickWithDebounce import com.shuwei.dish.match.utils.ext.gone @@ -33,8 +36,6 @@ import com.shuwei.dish.match.utils.ext.toJsonString import com.shuwei.dish.match.utils.ext.toType import com.shuwei.dish.match.utils.ext.toast import com.shuwei.dish.match.utils.ext.visible -import com.shuwei.dish.match.viewmodel.AppViewModel -import com.shuwei.dish.match.viewmodel.factory.AppFactory @SuppressLint("UseSparseArrays") class SubmitFoodActivity : BaseActivity() { @@ -69,7 +70,6 @@ class SubmitFoodActivity : BaseActivity() { super.onCreate(savedInstanceState) binding = ActivitySubmitFoodBinding.inflate(layoutInflater) setContentView(binding.root) - initViewModel() setHeaderBackground() intent.extras?.apply { food = getSerializable(FOOD_ITEM) as FoodRecord? @@ -95,6 +95,7 @@ class SubmitFoodActivity : BaseActivity() { } }) addViewListener() + initObserver() } private fun loadSeasoningFromLocal() { @@ -265,7 +266,7 @@ class SubmitFoodActivity : BaseActivity() { toast("未获取到调料信息") return } - if (realSeasoningData.isNotEmpty()&&realSeasoningData[0].goodsId.isNullOrBlank()) { + if (realSeasoningData.isNotEmpty()&&realSeasoningData[0].goodsId.isBlank()) { toast("还未设置调料信息,请去设置页面操作") return } @@ -316,7 +317,7 @@ class SubmitFoodActivity : BaseActivity() { toast("未获取到调料信息") return } - if (realSeasoningData.isNotEmpty()&&realSeasoningData[0].goodsId.isNullOrBlank()) { + if (realSeasoningData.isNotEmpty()&&realSeasoningData[0].goodsId.isBlank()) { toast("还未设置调料信息,请去设置页面操作") return } @@ -335,7 +336,7 @@ class SubmitFoodActivity : BaseActivity() { } cookFoodEntity.let { - it.stFoodInfoConstituteList = this@SubmitFoodActivity.goodsList + it.matchingConstituteInfoList = this@SubmitFoodActivity.goodsList it.dinnerType = when(it.dinnerType) { "1" -> "早餐" "2" -> "午餐" @@ -350,36 +351,20 @@ class SubmitFoodActivity : BaseActivity() { cookFoodEntity.foodId = "" goodsList?.forEach { it.foodId = "" } } - val json = cookFoodEntity.toJsonString() - Log.d(TAG, "submit: json=$json") - HttpUtil.postJson( - url = UrlConfig.SUBMIT_DISH, - json = json, - doSuccess = { - Log.d(TAG, "submit: postJson=$it") - submitSuccess(isSamplingData) - }, doFailure = { code, msg -> - Log.d(TAG, "submit: postJson:code=$code,msg=$msg") - toast(msg) - dismissLoading() - }) + netViewModel.submitCookFood(entity = cookFoodEntity.toDTO()) } private fun submitSuccess(isSamplingData: Boolean) { - if (food?.foodId.isNullOrBlank()) { - //本地无删除数据直接跳转页面 - dismissLoading() - jumpPage(isSamplingData, false) - return - } - //删除本地数据 - appViewModel.deleteCookFoodAndGoods( - cookMode = food!!.cookMode, - foodId = food!!.foodId!! - ) { - dismissLoading() - jumpPage(isSamplingData, false) + if (food?.foodId.isNullOrBlank().not()) { + //接口提交成功后异步删除本地草稿 + dbViewModel.markSubmittedAndDeleteAsync( + cookMode = food!!.cookMode, + foodId = food!!.foodId!!, + dinnerType = food!!.dinnerType ?: "0" + ) } + dismissLoading() + jumpPage(isSamplingData, false) } private fun jumpPage(isSampling: Boolean, isCooking: Boolean) { @@ -395,14 +380,26 @@ class SubmitFoodActivity : BaseActivity() { } } - private lateinit var appViewModel: AppViewModel - - private fun initViewModel() { - val db = BaseApp.instance!!.database - val factory = - AppFactory(AppRepository(db.appDao())) - appViewModel = - ViewModelProvider(this, factory)[AppViewModel::class.java] + private fun initObserver() { + lifecycleScope.launch { + lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) { + netViewModel.submitCookFoodState.collect { state -> + when (state) { + is UiState.Loading -> showLoading() + is UiState.Success -> { + Log.d(TAG, "submit: success") + submitSuccess(food?.cookMode == 1) + } + is UiState.Error -> { + Log.d(TAG, "submit: error code=${state.code}, msg=${state.msg}") + toast(state.msg) + dismissLoading() + } + else -> {} + } + } + } + } } override fun onResume() { diff --git a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt index 5d10a83..3ddb378 100644 --- a/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt +++ b/app/src/main/java/com/shuwei/dish/match/ui/fragment/DishListFragment.kt @@ -14,17 +14,13 @@ import com.shuwei.dish.match.base.BaseFragment import com.shuwei.dish.match.databinding.FragmentDishListBinding import com.shuwei.dish.match.databinding.LayoutEmptyViewBinding -import com.shuwei.dish.match.entity.FoodRecord -import com.shuwei.dish.match.entity.FoodRecordBean -import com.shuwei.dish.match.http.HttpUtil -import com.shuwei.dish.match.http.UrlConfig +import com.shuwei.dish.match.model.FoodRecord +import com.shuwei.dish.match.net.UiState import com.shuwei.dish.match.ui.PrepareCookActivity import com.shuwei.dish.match.ui.SelectDishActivity import com.shuwei.dish.match.ui.SubmitFoodActivity import com.shuwei.dish.match.utils.SwipeCallback import com.shuwei.dish.match.utils.ext.startActivity -import com.shuwei.dish.match.utils.ext.toJsonString -import com.shuwei.dish.match.utils.ext.toObject import com.shuwei.dish.match.utils.ext.toast import java.io.Serializable @@ -153,40 +149,35 @@ class DishListFragment : BaseFragment() { @Suppress("unchecked_cast") fun getDishList() { activity.showLoading() - val map = mapOf( -// "foodName" to input, + val param = mutableMapOf( "dinnerType" to getDinnerTypeText(), - "pageNo" to "$pageNo", - "pageSize" to "$pageSize", + "pageNo" to pageNo, + "pageSize" to pageSize, "canteenId" to BaseApp.canteenId ) - val sb = StringBuilder(UrlConfig.QUERY_FOOD_LIST).apply { - append("?") - map.forEach { (key, value) -> append("$key=$value&") } - } - sb.deleteCharAt(sb.length - 1) - HttpUtil.get( - url = sb.toString(), - doSuccess = { - loadDishList(it) - }, doFailure = { code, msg -> + activity.netViewModel.searchFoodListWithCallback( + param = param, + onResult = { state -> try { - if (isAdded.not()) { - return@get + if (isAdded.not()) return@searchFoodListWithCallback + when (state) { + is UiState.Success -> loadDishList(state.data) + is UiState.Error -> { + binding.refreshLayout.setEnableRefresh(true) + toast(state.msg) + finishRefresh() + if (pageNo == 1) { + loadEmptyView() + } + activity.delayDismissLoading() + } + else -> {} } - binding.refreshLayout.run { - setEnableRefresh(true) - } - toast(msg) - finishRefresh() - if (pageNo == 1) { - loadEmptyView() - } - activity.delayDismissLoading() } catch (e: Exception) { e.printStackTrace() } - }) + } + ) } private var emptyViewBinding: LayoutEmptyViewBinding? = null @@ -255,30 +246,22 @@ class DishListFragment : BaseFragment() { } @SuppressLint("NotifyDataSetChanged") - private fun loadDishList(data: Any) { + private fun loadDishList(records: MutableList?) { try { - if (isAdded.not()) { - return - } - binding.refreshLayout.run { - setEnableRefresh(true) - } + if (isAdded.not()) return + binding.refreshLayout.setEnableRefresh(true) activity.delayDismissLoading() finishRefresh() - val json = data.toJsonString() - val recordBean: FoodRecordBean? = json.toObject() - if (recordBean == null || recordBean.records.isNullOrEmpty()) { - //toast("暂未搜索到相关菜品信息") + if (records.isNullOrEmpty()) { if (pageNo == 1) { loadEmptyView() } return } - val records = recordBean.records if (pageNo == 1) { list.clear() } - list.addAll(records!!) + list.addAll(records) dishAdapter.notifyDataSetChanged() val isLoadMoreEnable = records.size >= pageSize binding.refreshLayout.setEnableLoadMore(isLoadMoreEnable) diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt index 7d4d5fb..5d0e0b5 100644 --- a/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/AppViewModel.kt @@ -2,14 +2,15 @@ package com.shuwei.dish.match.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.shuwei.dish.match.db.AppRepository -import com.shuwei.dish.match.entity.CookFoodEntity -import com.shuwei.dish.match.entity.CookFoodGoodsEntity -import com.shuwei.dish.match.entity.SeasoningEntity +import com.shuwei.dish.match.db.DbRepository +import com.shuwei.dish.match.db.entity.CookFoodEntity +import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity +import com.shuwei.dish.match.db.entity.SeasoningEntity import kotlinx.coroutines.launch -import kotlin.collections.forEach -class AppViewModel(private val rep: AppRepository) : ViewModel() { +class AppViewModel : ViewModel() { + + private val rep = DbRepository() fun updateCookFood(entity: CookFoodEntity) { viewModelScope.launch { @@ -17,9 +18,9 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { } } - fun getCookFoodById(foodId: String, cookMode: Int, action: (CookFoodEntity?) -> Unit) { + fun getCookFoodById(foodId: String, cookMode: Int, dinnerType: String = "0", action: (CookFoodEntity?) -> Unit) { viewModelScope.launch { - val data = rep.getCookFoodById(foodId, cookMode) + val data = rep.getCookFoodById(foodId, cookMode, dinnerType) action(data) } } @@ -42,17 +43,6 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { } } -// fun saveCookFood(cookMode: Int, entity: CookFoodEntity) { -// viewModelScope.launch { -// val data = rep.getCookFoodById(entity.foodId, cookMode) -// if (data == null) { -// rep.insertCookFood(entity) -// return@launch -// } -// rep.updateCookFood(entity) -// } -// } - fun saveCookFoodAndGoods( cookMode: Int, entity: CookFoodEntity, @@ -60,19 +50,17 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { onFinish: () -> Unit ) { viewModelScope.launch { - val data = rep.getCookFoodById(entity.foodId, cookMode) + val data = rep.getCookFoodById(entity.foodId, cookMode, entity.dinnerType?:"") if (data != null) { - //存在数据删除 - rep.deleteCookFood(entity.foodId, cookMode) + rep.deleteCookFood(entity.foodId, cookMode, entity.dinnerType?:"") rep.deleteCookFoodGoodsList(data.id, entity.foodId) } - //重新保存数据 rep.insertCookFood(entity) - val newData = rep.getCookFoodById(entity.foodId, cookMode) + val newData = rep.getCookFoodById(entity.foodId, cookMode, entity.dinnerType?:"") list?.let { it -> it.forEach { goods -> goods.id = 0 - goods.pid = newData?.id?:0 + goods.pid = newData?.id ?: 0 } rep.insertGoodsList(it) } @@ -83,28 +71,27 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { fun deleteCookFoodAndGoods( cookMode: Int, foodId: String, + dinnerType: String = "0", onFinish: () -> Unit ) { viewModelScope.launch { - val data = rep.getCookFoodById(foodId, cookMode) + val data = rep.getCookFoodById(foodId, cookMode, dinnerType) if (data != null) { - rep.deleteCookFood(foodId = foodId, cookMode = cookMode) + rep.deleteCookFood(foodId = foodId, cookMode = cookMode, dinnerType = dinnerType) rep.deleteCookFoodGoodsList(pid = data.id, foodId = foodId) } onFinish() } } - //-------------------------------------------------------------------------------------------------------- - - fun getCookFoodGoodsList( foodId: String, cookMode: Int, + dinnerType: String = "0", action: (list: MutableList) -> Unit ) { viewModelScope.launch { - val cookFood = rep.getCookFoodById(foodId, cookMode) + val cookFood = rep.getCookFoodById(foodId, cookMode, dinnerType) cookFood?.let { val list = rep.getCookFoodGoodsList(pid = it.id, foodId = it.foodId) action(list) @@ -112,43 +99,6 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { } } -// fun updateGoods(entity: CookFoodGoodsEntity) { -// viewModelScope.launch { -// rep.updateGoods(entity) -// } -// } - -// fun saveCookFoodGoods(list: MutableList) { -// viewModelScope.launch { -// val foodId = list[0].foodId -// val data: MutableList? = -// rep.getCookFoodGoodsList(foodId!!) -// if (data.isNullOrEmpty()) { -// rep.insertGoodsList(list) -// return@launch -// } -// val localDbSeasoningList = rep.getCookFoodGoodsListByType(foodId, 3) -// localDbSeasoningList.forEach { -// it.isDel = 1 -// //更新本地调料数据为已删除的状态 -// rep.updateGoods(it) -// } -// //筛选出调料数据保存,主辅材不会变更不需要再次处理 -// val saveSeasoningList = list.filter { it.materialType == 3 } -// rep.insertGoodsList(saveSeasoningList.toMutableList()) -// } -// } - - //-------------------------------------------------------------------------------------------------------- - -// fun addSeasoning(list: MutableList, callback: () -> Unit) { -// viewModelScope.launch { -// rep.deleteBatch() -// rep.insertSeasoningList(items = list) -// callback() -// } -// } - private var isProcessing = false fun loadSeasoning(action: (MutableList) -> Unit) { viewModelScope.launch { @@ -166,25 +116,17 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { } } - fun clearAllSeasoning(action:()->Unit) { + fun clearAllSeasoning(action: () -> Unit) { viewModelScope.launch { rep.clearAllSeasoning() action() } } -// fun updateAll(list: MutableList) { -// viewModelScope.launch { -// list.forEach { -// rep.updateSeasoning(it) -// } -// } -// } - fun saveSeasoning(entity: SeasoningEntity, block: () -> Unit) { viewModelScope.launch { val list = rep.getSeasoningBySort(entity.sort) - val size = list?.size?:0 + val size = list?.size ?: 0 if (size == 1) { rep.updateSeasoning(entity.also { it.id = list!![0].id }) block() @@ -201,6 +143,4 @@ class AppViewModel(private val rep: AppRepository) : ViewModel() { block() } } - - //-------------------------------------------------------------------------------------------------------- -} \ No newline at end of file +} diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt index 5322ca2..43b7cd9 100644 --- a/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodGoodsViewModel.kt @@ -3,7 +3,7 @@ //import androidx.lifecycle.ViewModel //import androidx.lifecycle.viewModelScope //import com.shuwei.dish.match.db.CookFoodGoodsRepository -//import com.shuwei.dish.match.entity.CookFoodGoodsEntity +//import com.shuwei.dish.match.db.entity.CookFoodGoodsEntity //import kotlinx.coroutines.launch // //class CookFoodGoodsViewModel(private val rep: CookFoodGoodsRepository) : ViewModel() { diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt index a6c8468..14ecccb 100644 --- a/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/CookFoodViewModel.kt @@ -3,7 +3,7 @@ //import androidx.lifecycle.ViewModel //import androidx.lifecycle.viewModelScope //import com.shuwei.dish.match.db.CookFoodRepository -//import com.shuwei.dish.match.entity.CookFoodEntity +//import com.shuwei.dish.match.db.entity.CookFoodEntity //import kotlinx.coroutines.launch // //class CookFoodViewModel(private val rep: CookFoodRepository) : ViewModel() { diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt index cbb8e0d..b20f289 100644 --- a/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/DeviceSettingViewModel.kt @@ -3,7 +3,7 @@ //import androidx.lifecycle.ViewModel //import androidx.lifecycle.viewModelScope //import com.shuwei.dish.match.db.SeasoningRepository -//import com.shuwei.dish.match.entity.SeasoningEntity +//import com.shuwei.dish.match.db.entity.SeasoningEntity //import kotlinx.coroutines.delay //import kotlinx.coroutines.launch // diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt index 2919895..6f2b9d3 100644 --- a/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt +++ b/app/src/main/java/com/shuwei/dish/match/viewmodel/SubmitDishViewModel.kt @@ -3,8 +3,8 @@ //import androidx.lifecycle.ViewModel //import androidx.lifecycle.viewModelScope //import com.shuwei.dish.match.db.SeasoningRepository -//import com.shuwei.dish.match.entity.CookFoodEntity -//import com.shuwei.dish.match.entity.SeasoningEntity +//import com.shuwei.dish.match.db.entity.CookFoodEntity +//import com.shuwei.dish.match.db.entity.SeasoningEntity //import kotlinx.coroutines.launch // //class SubmitDishViewModel(private val rep: SeasoningRepository) : ViewModel() { diff --git a/app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt b/app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt deleted file mode 100644 index 1ca32fc..0000000 --- a/app/src/main/java/com/shuwei/dish/match/viewmodel/factory/VMFactory.kt +++ /dev/null @@ -1,43 +0,0 @@ -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.viewmodel.AppViewModel - -@Suppress("UNCHECKED_CAST") -class AppFactory(private val repo: AppRepository) : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return AppViewModel(repo) as T - } -} - -// -//@Suppress("UNCHECKED_CAST") -//class DeviceSettingFactory(private val repo: SeasoningRepository) : ViewModelProvider.Factory { -// override fun create(modelClass: Class): T { -// return DeviceSettingViewModel(repo) as T -// } -//} -// -////@Suppress("UNCHECKED_CAST") -////class SubmitDishFactory(private val repo: SeasoningRepository) : ViewModelProvider.Factory { -//// override fun create(modelClass: Class): T { -//// return SubmitDishViewModel(repo) as T -//// } -////} -// -//@Suppress("UNCHECKED_CAST") -//class CookFoodFactory(private val repo: CookFoodRepository) : ViewModelProvider.Factory { -// override fun create(modelClass: Class): T { -// return CookFoodViewModel(repo) as T -// } -//} -// -//@Suppress("UNCHECKED_CAST") -//class CookFoodGoodsFactory(private val repo: CookFoodGoodsRepository) : ViewModelProvider.Factory { -// override fun create(modelClass: Class): T { -// return CookFoodGoodsViewModel(repo) as T -// } -//} -