refactor(network): 重构网络请求配置和API接口实现
- 将BASE_URL改为使用GlobalData.PROD_BASE_URL常量 - 更新ApiClient中的URL匹配规则以支持新的营养模块接口 - 将ApiServiceV2中的接口路径从/neglect/booth改为/nutrition/neglect/booth - 使用@Url注解动态传入完整URL地址 - 在CollectedFoodActivity中切换到NetViewModelV2和新API模型 - 更新采集功能的数据模型和接口调用方式 - 调整人脸数据获取逻辑以支持增量和全量同步 - 优化文件上传和删除操作的参数传递方式
This commit is contained in:
@@ -22,7 +22,8 @@ object GlobalData {
|
|||||||
/**
|
/**
|
||||||
* 具体业务 BaseUrl
|
* 具体业务 BaseUrl
|
||||||
*/
|
*/
|
||||||
const val TEST_BASE_URL = "http://192.168.1.201:14801"
|
// const val TEST_BASE_URL = "http://192.168.1.201:14801"
|
||||||
|
const val TEST_BASE_URL = "http://192.168.10.101:24801"
|
||||||
const val UAT_BASE_URL = "https://dev.yixiong-tech.com:8083"
|
const val UAT_BASE_URL = "https://dev.yixiong-tech.com:8083"
|
||||||
const val PROD_BASE_URL = "https://api.dm.yixiong-tech.com:8443"
|
const val PROD_BASE_URL = "https://api.dm.yixiong-tech.com:8443"
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import androidx.activity.viewModels
|
|||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.sw.dualscreen.GlobalData
|
|
||||||
import com.sw.dualscreen.R
|
import com.sw.dualscreen.R
|
||||||
import com.sw.dualscreen.adapter.CollectedFoodNewAdapter
|
import com.sw.dualscreen.adapter.CollectedFoodNewAdapter
|
||||||
import com.sw.dualscreen.databinding.ActivityCollectedFoodBinding
|
import com.sw.dualscreen.databinding.ActivityCollectedFoodBinding
|
||||||
@@ -14,10 +13,10 @@ import com.sw.dualscreen.ext.addOnActionSearchListener
|
|||||||
import com.sw.dualscreen.ext.gone
|
import com.sw.dualscreen.ext.gone
|
||||||
import com.sw.dualscreen.ext.hideKeyboard
|
import com.sw.dualscreen.ext.hideKeyboard
|
||||||
import com.sw.dualscreen.ext.visible
|
import com.sw.dualscreen.ext.visible
|
||||||
import com.sw.dualscreen.objbox.CollectedFoodInfo
|
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
|
||||||
import com.sw.dualscreen.objbox.ObjectBox
|
import com.sw.dualscreen.objbox.ObjectBox
|
||||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
|||||||
private const val PAGE_SIZE = 100
|
private const val PAGE_SIZE = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel by viewModels<UserViewModel>()
|
private val viewModel by viewModels<NetViewModelV2>()
|
||||||
override fun getViewModel(): BaseViewModel {
|
override fun getViewModel(): BaseViewModel {
|
||||||
return viewModel
|
return viewModel
|
||||||
}
|
}
|
||||||
@@ -36,7 +35,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
|||||||
return ActivityCollectedFoodBinding.inflate(layoutInflater)
|
return ActivityCollectedFoodBinding.inflate(layoutInflater)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val list: MutableList<CollectedFoodInfo> = mutableListOf()
|
private val list: MutableList<CollectedFoodV2> = mutableListOf()
|
||||||
private val adapter by lazy {
|
private val adapter by lazy {
|
||||||
CollectedFoodNewAdapter(list).apply {
|
CollectedFoodNewAdapter(list).apply {
|
||||||
// isStateViewEnable = true
|
// isStateViewEnable = true
|
||||||
@@ -127,19 +126,26 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
|||||||
// loadEmptyView()
|
// loadEmptyView()
|
||||||
// }
|
// }
|
||||||
|
|
||||||
viewModel.getCollectedFoodList(
|
viewModel.getCollectPage(
|
||||||
pageNo = pageNo,
|
pageNum = pageNo.toLong(),
|
||||||
pageSize = PAGE_SIZE,
|
pageSize = PAGE_SIZE.toLong(),
|
||||||
foodName = searchName ?: ""
|
foodName = searchName,
|
||||||
) { items ->
|
onSuccess = { items ->
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
loadFoodList(items)
|
loadFoodList(items.onEach { it.foodCount = 1 })
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onFailure = {
|
||||||
|
runOnUiThread {
|
||||||
|
finishRefresh()
|
||||||
|
ToastUtils.showToast(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
private fun loadFoodList(items: List<CollectedFoodInfo>) {
|
private fun loadFoodList(items: List<CollectedFoodV2>) {
|
||||||
finishRefresh()
|
finishRefresh()
|
||||||
if (pageNo == 1 && items.isEmpty()) {
|
if (pageNo == 1 && items.isEmpty()) {
|
||||||
list.clear()
|
list.clear()
|
||||||
@@ -191,7 +197,7 @@ class CollectedFoodActivity : BaseActivity<ActivityCollectedFoodBinding>() {
|
|||||||
private fun deleteGoods(position: Int) {
|
private fun deleteGoods(position: Int) {
|
||||||
val food = list[position]
|
val food = list[position]
|
||||||
showWaitingDialog("加载中……")
|
showWaitingDialog("加载中……")
|
||||||
viewModel.deleteCollectFood(food.foodId, GlobalData.foodModelVersion) { deleteSuccess ->
|
viewModel.deleteCollect(food.foodId, food.version) { deleteSuccess ->
|
||||||
if (deleteSuccess) {
|
if (deleteSuccess) {
|
||||||
// Thread {}.start()
|
// Thread {}.start()
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import com.sw.dualscreen.utils.L
|
|||||||
import com.sw.dualscreen.utils.NetworkUtils
|
import com.sw.dualscreen.utils.NetworkUtils
|
||||||
import com.sw.dualscreen.utils.SpTool
|
import com.sw.dualscreen.utils.SpTool
|
||||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import com.sw.plate.utils.AppUtil
|
import com.sw.plate.utils.AppUtil
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -29,7 +29,7 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
|||||||
const val PAGE_SIZE = 100
|
const val PAGE_SIZE = 100
|
||||||
}
|
}
|
||||||
|
|
||||||
private val userViewModel by viewModels<UserViewModel>()
|
private val userViewModel by viewModels<NetViewModelV2>()
|
||||||
private var pageNum = 1
|
private var pageNum = 1
|
||||||
|
|
||||||
override fun getViewModel(): BaseViewModel {
|
override fun getViewModel(): BaseViewModel {
|
||||||
@@ -107,6 +107,7 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
checkCameraPermissionAndGo()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goMainActivity() {
|
private fun goMainActivity() {
|
||||||
@@ -189,23 +190,23 @@ class InitActivity : BaseActivity<ActivityInitBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun getDeviceConfig() {
|
private fun getDeviceConfig() {
|
||||||
userViewModel.getDeviceConfig { deviceConfig ->
|
userViewModel.getDeviceConfig(
|
||||||
|
onSuccess = { deviceConfig ->
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
if (deviceConfig == null) {
|
if (deviceConfig == null) {
|
||||||
ToastUtils.showToast("获取设备配置数据失败")
|
ToastUtils.showToast("获取设备配置数据失败")
|
||||||
return@runOnUiThread
|
return@runOnUiThread
|
||||||
}
|
}
|
||||||
//var settlementMode = 1
|
|
||||||
//结算模式:1-独立支付,2-联合结算
|
|
||||||
val settlementMode = if (deviceConfig.payType == 2) 0 else 1
|
val settlementMode = if (deviceConfig.payType == 2) 0 else 1
|
||||||
// TODO: 2022/10/13 修改为独立支付
|
|
||||||
SpTool.settlementMode = settlementMode
|
SpTool.settlementMode = settlementMode
|
||||||
|
|
||||||
GlobalData.appId = deviceConfig.arcsoftAppId ?: ""
|
GlobalData.appId = deviceConfig.arcsoftAppId ?: ""
|
||||||
GlobalData.sdkKey = deviceConfig.arcsoftSdkKey ?: ""
|
GlobalData.sdkKey = deviceConfig.arcsoftSdkKey ?: ""
|
||||||
GlobalData.activeKey = deviceConfig.arcsoftActiveKey ?: ""
|
GlobalData.activeKey = deviceConfig.arcsoftActiveKey ?: ""
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onFailure = { runOnUiThread { ToastUtils.showToast(it) } }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loadNetworkErrorDialog() {
|
private fun loadNetworkErrorDialog() {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ import com.sw.dualscreen.model.response.FoodOrder
|
|||||||
import com.sw.dualscreen.model.response.PaySuccessEvent
|
import com.sw.dualscreen.model.response.PaySuccessEvent
|
||||||
import com.sw.dualscreen.model.response.ResetRecognizeEvent
|
import com.sw.dualscreen.model.response.ResetRecognizeEvent
|
||||||
import com.sw.dualscreen.model.response.UpdateRefreshEvent
|
import com.sw.dualscreen.model.response.UpdateRefreshEvent
|
||||||
import com.sw.dualscreen.model.response.UserFaceModel
|
import com.sw.dualscreen.model.response.v2.FaceVO
|
||||||
import com.sw.dualscreen.objbox.Food
|
import com.sw.dualscreen.objbox.Food
|
||||||
import com.sw.dualscreen.objbox.FoodModule
|
import com.sw.dualscreen.objbox.FoodModule
|
||||||
import com.sw.dualscreen.objbox.FoodModule.IdNameScore
|
import com.sw.dualscreen.objbox.FoodModule.IdNameScore
|
||||||
@@ -67,7 +67,7 @@ import com.sw.dualscreen.utils.SPUtil
|
|||||||
import com.sw.dualscreen.utils.SpTool
|
import com.sw.dualscreen.utils.SpTool
|
||||||
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
import com.sw.dualscreen.view.CustomBottomSheetDialog
|
||||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import com.sw.plate.utils.Base64
|
import com.sw.plate.utils.Base64
|
||||||
import com.sw.plate.utils.LightManager
|
import com.sw.plate.utils.LightManager
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
@@ -81,8 +81,6 @@ import kotlinx.coroutines.delay
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import okhttp3.RequestBody
|
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
import org.greenrobot.eventbus.Subscribe
|
import org.greenrobot.eventbus.Subscribe
|
||||||
import org.greenrobot.eventbus.ThreadMode
|
import org.greenrobot.eventbus.ThreadMode
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
@@ -109,7 +107,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
const val WEIGHT_RESET_RECOGNIZE = 10
|
const val WEIGHT_RESET_RECOGNIZE = 10
|
||||||
}
|
}
|
||||||
|
|
||||||
private val viewModel by viewModels<UserViewModel>()
|
private val viewModel by viewModels<NetViewModelV2>()
|
||||||
private var imageCapture: ImageCapture? = null
|
private var imageCapture: ImageCapture? = null
|
||||||
var isAnalyzing = true // 控制是否进行图像分析
|
var isAnalyzing = true // 控制是否进行图像分析
|
||||||
|
|
||||||
@@ -208,7 +206,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
addBackEventListener()
|
addBackEventListener()
|
||||||
initView()
|
initView()
|
||||||
if (SpTool.firstGetFace) {
|
if (SpTool.firstGetFace) {
|
||||||
viewModel.getUserFaceCache(pageNo = 1)
|
viewModel.getFacePage(pageNum = 1)
|
||||||
}
|
}
|
||||||
setupCamera()
|
setupCamera()
|
||||||
initData()
|
initData()
|
||||||
@@ -273,7 +271,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
userViewModel = viewModel,
|
userViewModel = viewModel,
|
||||||
lifecycleScope = lifecycleScope,
|
lifecycleScope = lifecycleScope,
|
||||||
successBlock = {
|
successBlock = {
|
||||||
viewModel.getUserFaceCache(pageNo = 1)
|
hideWaitingDialog()
|
||||||
},
|
},
|
||||||
failureBlock = {
|
failureBlock = {
|
||||||
hideWaitingDialog()
|
hideWaitingDialog()
|
||||||
@@ -281,7 +279,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
viewModel.getUserFaceCache(pageNo = 1, onSuccess = {
|
viewModel.getFacePage(pageNum = 1, onSuccess = {
|
||||||
recognizeViewModel.refreshFaceList()
|
recognizeViewModel.refreshFaceList()
|
||||||
hideWaitingDialog()
|
hideWaitingDialog()
|
||||||
ToastUtils.showToast("人脸数据已更新")
|
ToastUtils.showToast("人脸数据已更新")
|
||||||
@@ -1111,14 +1109,12 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
log("是否null判断,imageFile == null:${imageFile == null},imageVector == null:${imageVector == null}")
|
log("是否null判断,imageFile == null:${imageFile == null},imageVector == null:${imageVector == null}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val foodId = foodInfo.foodId
|
val foodId = foodInfo.foodId.toLongOrNull() ?: run {
|
||||||
|
loadRemindDialog("foodId 格式错误: ${foodInfo.foodId}")
|
||||||
|
return
|
||||||
|
}
|
||||||
val foodName = foodInfo.foodName ?: ""
|
val foodName = foodInfo.foodName ?: ""
|
||||||
val foodModelVersion = GlobalData.foodModelVersion
|
val foodModelVersion = GlobalData.foodModelVersion
|
||||||
val params = HashMap<String, RequestBody>()
|
|
||||||
params["foodId"] = foodId.toRequestBody()
|
|
||||||
params["foodName"] = foodName.toRequestBody()
|
|
||||||
params["version"] = foodModelVersion.toRequestBody()
|
|
||||||
val files = listOf(imageFile)
|
|
||||||
val vectors = listOf(imageVector)
|
val vectors = listOf(imageVector)
|
||||||
val foodVectorList = vectors.map {
|
val foodVectorList = vectors.map {
|
||||||
it.joinToString(
|
it.joinToString(
|
||||||
@@ -1128,8 +1124,13 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
val foodVectorJson =
|
val foodVectorJson =
|
||||||
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
||||||
log("json=$foodVectorJson")
|
log("json=$foodVectorJson")
|
||||||
params["foodVector"] = foodVectorJson.toRequestBody()
|
val idList = viewModel.uploadCollect(
|
||||||
val idList = viewModel.uploadCollectFoodPics(files, params)
|
foodId = foodId,
|
||||||
|
foodName = foodName,
|
||||||
|
version = foodModelVersion,
|
||||||
|
foodVector = foodVectorJson,
|
||||||
|
fileList = listOf(imageFile)
|
||||||
|
)
|
||||||
if (idList.isNullOrEmpty()) {
|
if (idList.isNullOrEmpty()) {
|
||||||
loadRemindDialog("上传失败,未返回向量数据id")
|
loadRemindDialog("上传失败,未返回向量数据id")
|
||||||
log("idList为空")
|
log("idList为空")
|
||||||
@@ -1146,7 +1147,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
ObjectBox.put(
|
ObjectBox.put(
|
||||||
Food(
|
Food(
|
||||||
collectId = idList[0],
|
collectId = idList[0],
|
||||||
foodId = foodId,
|
foodId = "$foodId",
|
||||||
foodName = foodName,
|
foodName = foodName,
|
||||||
foodVector = imageVector,
|
foodVector = imageVector,
|
||||||
version = foodModelVersion
|
version = foodModelVersion
|
||||||
@@ -1204,7 +1205,7 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
//if (timestamp == 0L) {
|
//if (timestamp == 0L) {
|
||||||
// return@startIntervalTaskWithInitialDelay
|
// return@startIntervalTaskWithInitialDelay
|
||||||
//}
|
//}
|
||||||
viewModel.getFaceIncrementList(
|
viewModel.getFaceIncrement(
|
||||||
// pageNo = taskPageNo,
|
// pageNo = taskPageNo,
|
||||||
timestamp = timestamp,
|
timestamp = timestamp,
|
||||||
onAllQueryFinished = {
|
onAllQueryFinished = {
|
||||||
@@ -1223,21 +1224,21 @@ class MainActivity : BaseActivity<ActivityMainBinding>() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateFaceData(list: List<UserFaceModel>) {
|
private fun updateFaceData(list: List<FaceVO>) {
|
||||||
val faceList = mutableListOf<FaceEntity>()
|
val faceList = mutableListOf<FaceEntity>()
|
||||||
try {
|
try {
|
||||||
list.forEach { model ->
|
list.forEach { vo ->
|
||||||
if (model.faceDeleted == true) {
|
if (vo.faceDeleted == true) {
|
||||||
//删除数据
|
//删除数据
|
||||||
FaceDatabase.getInstance(this).faceDao().deleteFaceById(model.userId)
|
FaceDatabase.getInstance(this).faceDao().deleteFaceById(vo.userId)
|
||||||
} else {
|
} else {
|
||||||
//保存数据
|
//保存数据
|
||||||
val faceEntity = FaceEntity(
|
val faceEntity = FaceEntity(
|
||||||
model.userId, null, Base64.decode(model.faceFeatureStr)
|
vo.userId, null, Base64.decode(vo.faceFeature)
|
||||||
).also {
|
).also {
|
||||||
//1-会员、2-临时用户
|
//1-会员、2-临时用户
|
||||||
it.userType = if (model.member) "1" else "2"
|
it.userType = if (vo.member == true) "1" else "2"
|
||||||
it.cardNo = model.cardNo
|
it.cardNo = vo.cardNo
|
||||||
}
|
}
|
||||||
faceList.add(faceEntity)
|
faceList.add(faceEntity)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import com.sw.dualscreen.utils.Debouncer
|
|||||||
import com.sw.dualscreen.utils.SPUtil
|
import com.sw.dualscreen.utils.SPUtil
|
||||||
import com.sw.dualscreen.utils.SoundPoolUtil
|
import com.sw.dualscreen.utils.SoundPoolUtil
|
||||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
@@ -43,7 +43,7 @@ class PayActivity : BaseActivity<ActivityPayBinding>() {
|
|||||||
const val PAY_SUCCESS = "pay_success"
|
const val PAY_SUCCESS = "pay_success"
|
||||||
}
|
}
|
||||||
|
|
||||||
val userViewModel by viewModels<UserViewModel>()
|
val userViewModel by viewModels<NetViewModelV2>()
|
||||||
val recognizeViewModel by viewModels<RecognizeViewModel>()
|
val recognizeViewModel by viewModels<RecognizeViewModel>()
|
||||||
|
|
||||||
override fun getViewModel(): BaseViewModel {
|
override fun getViewModel(): BaseViewModel {
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ import com.sw.dualscreen.model.response.FoodInfo
|
|||||||
import com.sw.dualscreen.model.response.ClickBackEvent
|
import com.sw.dualscreen.model.response.ClickBackEvent
|
||||||
import com.sw.dualscreen.model.response.UpdateRefreshEvent
|
import com.sw.dualscreen.model.response.UpdateRefreshEvent
|
||||||
import com.sw.dualscreen.viewmodel.BaseViewModel
|
import com.sw.dualscreen.viewmodel.BaseViewModel
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import org.greenrobot.eventbus.EventBus
|
import org.greenrobot.eventbus.EventBus
|
||||||
|
|
||||||
class SettingActivity : BaseActivity<ActivitySettingBinding>() {
|
class SettingActivity : BaseActivity<ActivitySettingBinding>() {
|
||||||
|
|
||||||
val viewModel by viewModels<UserViewModel>()
|
val viewModel by viewModels<NetViewModelV2>()
|
||||||
override fun getViewModel(): BaseViewModel {
|
override fun getViewModel(): BaseViewModel {
|
||||||
return viewModel
|
return viewModel
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,8 +36,6 @@ import com.sw.dualscreen.utils.ImageUtil
|
|||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import okhttp3.RequestBody
|
|
||||||
import okhttp3.RequestBody.Companion.toRequestBody
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
@@ -263,14 +261,15 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
|||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val totalFileCount = foodCollectionList.count { it.imageFile != null }
|
val totalFileCount = foodCollectionList.count { it.imageFile != null }
|
||||||
settingActivity?.showWaitingDialog2("图片上传中0/$totalFileCount")
|
settingActivity?.showWaitingDialog2("图片上传中0/$totalFileCount")
|
||||||
val params = HashMap<String, RequestBody>()
|
val foodId = checkedItem!!.foodId.toLongOrNull() ?: run {
|
||||||
//params["placeId"] = restId.toRequestBody()
|
ToastUtils.showToast("foodId 格式错误")
|
||||||
params["foodId"] = checkedItem!!.foodId.toRequestBody()
|
return@launch
|
||||||
params["foodName"] = checkedItem!!.foodName!!.toRequestBody()
|
}
|
||||||
params["version"] = GlobalData.foodModelVersion.toRequestBody()
|
val foodName = checkedItem!!.foodName ?: ""
|
||||||
|
val version = GlobalData.foodModelVersion
|
||||||
|
|
||||||
ImageUploader(totalList = foodCollectionList, uploadImage = { batch ->
|
ImageUploader(totalList = foodCollectionList, uploadImage = { batch ->
|
||||||
val files = batch.map { it.imageFile }
|
val files = batch.mapNotNull { it.imageFile }
|
||||||
val foodVectorList = batch.filter { it.imageVector != null }.map {
|
val foodVectorList = batch.filter { it.imageVector != null }.map {
|
||||||
it.imageVector!!.joinToString(
|
it.imageVector!!.joinToString(
|
||||||
separator = ",", prefix = "[", postfix = "]"
|
separator = ",", prefix = "[", postfix = "]"
|
||||||
@@ -279,8 +278,13 @@ class CollectFragment : BaseFragment<FragmentCollectBinding>() {
|
|||||||
val foodVectorJson =
|
val foodVectorJson =
|
||||||
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
foodVectorList.joinToString(separator = ",", prefix = "[", postfix = "]")
|
||||||
Timber.tag(TAG).d("json=$foodVectorJson")
|
Timber.tag(TAG).d("json=$foodVectorJson")
|
||||||
params["foodVector"] = foodVectorJson.toRequestBody()
|
settingActivity?.viewModel?.uploadCollect(
|
||||||
settingActivity?.viewModel?.uploadCollectFoodPics(files, params)
|
foodId = foodId,
|
||||||
|
foodName = foodName,
|
||||||
|
version = version,
|
||||||
|
foodVector = foodVectorJson,
|
||||||
|
fileList = files
|
||||||
|
)
|
||||||
}, onProgress = { count, batch, idList ->
|
}, onProgress = { count, batch, idList ->
|
||||||
//batch.forEach {
|
//batch.forEach {
|
||||||
// it.uploadSuccess = true
|
// it.uploadSuccess = true
|
||||||
|
|||||||
@@ -8,10 +8,10 @@ import com.chad.library.adapter4.viewholder.QuickViewHolder
|
|||||||
import com.sw.dualscreen.databinding.ListItemCollectedFoodBinding
|
import com.sw.dualscreen.databinding.ListItemCollectedFoodBinding
|
||||||
import com.sw.dualscreen.ext.gone
|
import com.sw.dualscreen.ext.gone
|
||||||
import com.sw.dualscreen.ext.visible
|
import com.sw.dualscreen.ext.visible
|
||||||
import com.sw.dualscreen.objbox.CollectedFoodInfo
|
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
|
||||||
|
|
||||||
class CollectedFoodNewAdapter(var list: MutableList<CollectedFoodInfo>) :
|
class CollectedFoodNewAdapter(var list: MutableList<CollectedFoodV2>) :
|
||||||
BaseQuickAdapter<CollectedFoodInfo, CollectedFoodNewAdapter.VH>(list) {
|
BaseQuickAdapter<CollectedFoodV2, CollectedFoodNewAdapter.VH>(list) {
|
||||||
|
|
||||||
inner class VH(var binding: ListItemCollectedFoodBinding) : QuickViewHolder(binding.root)
|
inner class VH(var binding: ListItemCollectedFoodBinding) : QuickViewHolder(binding.root)
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ class CollectedFoodNewAdapter(var list: MutableList<CollectedFoodInfo>) :
|
|||||||
return VH(binding)
|
return VH(binding)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: VH, position: Int, item: CollectedFoodInfo?) {
|
override fun onBindViewHolder(holder: VH, position: Int, item: CollectedFoodV2?) {
|
||||||
holder.binding.tvFoodName.text = item?.foodName
|
holder.binding.tvFoodName.text = item?.foodName
|
||||||
holder.binding.tvCollectedNum.text = "已采集${item?.foodCount}"
|
holder.binding.tvCollectedNum.text = "已采集${item?.foodCount}"
|
||||||
holder.binding.divider.run {
|
holder.binding.divider.run {
|
||||||
|
|||||||
@@ -13,5 +13,7 @@ data class CollectedFoodV2(
|
|||||||
val foodName: String?,
|
val foodName: String?,
|
||||||
val version: String?,
|
val version: String?,
|
||||||
val foodVector: String?,
|
val foodVector: String?,
|
||||||
val picUrls: List<String>?
|
val picUrls: List<String>?,
|
||||||
|
/** 已采集数量(客户端手动赋值,非接口字段,用于兼容旧版 UI) */
|
||||||
|
var foodCount: Int = 0
|
||||||
) : Parcelable
|
) : Parcelable
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.sw.dualscreen.network
|
package com.sw.dualscreen.network
|
||||||
|
|
||||||
import com.sw.dualscreen.BuildConfig
|
import com.sw.dualscreen.BuildConfig
|
||||||
|
import com.sw.dualscreen.GlobalData
|
||||||
import com.sw.dualscreen.MyApp
|
import com.sw.dualscreen.MyApp
|
||||||
import com.sw.dualscreen.network.api.ApiService
|
import com.sw.dualscreen.network.api.ApiService
|
||||||
import com.sw.dualscreen.network.api.ApiServiceV2
|
import com.sw.dualscreen.network.api.ApiServiceV2
|
||||||
@@ -17,7 +18,8 @@ import timber.log.Timber
|
|||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
object ApiClient {
|
object ApiClient {
|
||||||
private const val BASE_URL = "http://device.shuziweidao.com:8889/"
|
// private const val BASE_URL = "http://device.shuziweidao.com:8889/"
|
||||||
|
private const val BASE_URL = GlobalData.PROD_BASE_URL
|
||||||
|
|
||||||
private const val TIME_OUT = 30L
|
private const val TIME_OUT = 30L
|
||||||
|
|
||||||
@@ -25,12 +27,15 @@ object ApiClient {
|
|||||||
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
|
.connectTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
.readTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
.writeTimeout(TIME_OUT, TimeUnit.SECONDS)
|
||||||
.addNetworkInterceptor(Interceptor {chain ->
|
.addNetworkInterceptor(Interceptor { chain ->
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
val url = request.url.toString()
|
val url = request.url.toString()
|
||||||
val tag = if (url.contains("terminal/neglect/common/app/faceFeature/increment/list")) "faceIncrement"
|
val tag = if (
|
||||||
|
url.contains("terminal/neglect/common/app/faceFeature/increment/list") ||
|
||||||
|
url.contains("nutrition/neglect/booth/face/increment")
|
||||||
|
) "faceIncrement"
|
||||||
else if (url.contains("terminal/neglect/pay/app/turnOrderInfo")) "turnOrderInfo"
|
else if (url.contains("terminal/neglect/pay/app/turnOrderInfo")) "turnOrderInfo"
|
||||||
else if (url.contains("neglect/booth/")) "ApiV2"
|
else if(url.contains("nutrition/neglect")) "V2"
|
||||||
else "ApiClient"
|
else "ApiClient"
|
||||||
val loggingInterceptor = HttpLoggingInterceptor(logger = {
|
val loggingInterceptor = HttpLoggingInterceptor(logger = {
|
||||||
Timber.tag(tag).d("okhttp logger ==>${it}")
|
Timber.tag(tag).d("okhttp logger ==>${it}")
|
||||||
|
|||||||
@@ -20,81 +20,97 @@ import retrofit2.http.Multipart
|
|||||||
import retrofit2.http.POST
|
import retrofit2.http.POST
|
||||||
import retrofit2.http.Part
|
import retrofit2.http.Part
|
||||||
import retrofit2.http.PartMap
|
import retrofit2.http.PartMap
|
||||||
|
import retrofit2.http.Url
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新系统 API 接口服务
|
* 新系统 API 接口服务
|
||||||
* 基于 /neglect/booth 模块前缀
|
* 基于 /nutrition/neglect/booth 模块前缀
|
||||||
* 文档版本:2026-05-27
|
* 文档版本:2026-05-27
|
||||||
*/
|
*/
|
||||||
interface ApiServiceV2 {
|
interface ApiServiceV2 {
|
||||||
|
|
||||||
@GET("/neglect/booth/device/config")
|
@GET
|
||||||
suspend fun getDeviceConfig(): ApiResponse<DeviceConfig?>
|
suspend fun getDeviceConfig(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/device/config"
|
||||||
|
): ApiResponse<DeviceConfig?>
|
||||||
|
|
||||||
@POST("/neglect/booth/face/page")
|
@POST
|
||||||
suspend fun getFacePage(
|
suspend fun getFacePage(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/face/page",
|
||||||
@Body request: Map<String, Long>
|
@Body request: Map<String, Long>
|
||||||
): ApiResponse<List<FaceVO>?>
|
): ApiResponse<List<FaceVO>?>
|
||||||
|
|
||||||
@POST("/neglect/booth/face/increment")
|
@POST
|
||||||
suspend fun getFaceIncrement(
|
suspend fun getFaceIncrement(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/face/increment",
|
||||||
@Body request: Map<String, Long>
|
@Body request: Map<String, Long>
|
||||||
): ApiResponse<List<FaceVO>?>
|
): ApiResponse<List<FaceVO>?>
|
||||||
|
|
||||||
@POST("/neglect/booth/food/by-names")
|
@POST
|
||||||
suspend fun getFoodByNames(
|
suspend fun getFoodByNames(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/food/by-names",
|
||||||
@Body request: FoodSearchReq
|
@Body request: FoodSearchReq
|
||||||
): ApiResponse<List<NewFoodInfo>?>
|
): ApiResponse<List<NewFoodInfo>?>
|
||||||
|
|
||||||
@POST("/neglect/booth/user/current-food")
|
@POST
|
||||||
suspend fun getUserCurrentFood(
|
suspend fun getUserCurrentFood(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/user/current-food",
|
||||||
@Body request: Map<String, Long>
|
@Body request: Map<String, Long>
|
||||||
): ApiResponse<UserNutrition?>
|
): ApiResponse<UserNutrition?>
|
||||||
|
|
||||||
@POST("/neglect/booth/order/place")
|
@POST
|
||||||
suspend fun placeOrder(
|
suspend fun placeOrder(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/order/place",
|
||||||
@Body request: PlaceOrderRequest
|
@Body request: PlaceOrderRequest
|
||||||
): ApiResponse<String?>
|
): ApiResponse<String?>
|
||||||
|
|
||||||
@POST("/neglect/booth/order/settlement")
|
@POST
|
||||||
suspend fun getSettlementOrders(
|
suspend fun getSettlementOrders(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/order/settlement",
|
||||||
@Body request: Map<String, Long>
|
@Body request: Map<String, Long>
|
||||||
): ApiResponse<SettlementOrder?>
|
): ApiResponse<SettlementOrder?>
|
||||||
|
|
||||||
@POST("/neglect/booth/member/info")
|
@POST
|
||||||
suspend fun getMemberInfo(
|
suspend fun getMemberInfo(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/member/info",
|
||||||
@Body request: Map<String, Long>
|
@Body request: Map<String, Long>
|
||||||
): ApiResponse<NewMemberInfo?>
|
): ApiResponse<NewMemberInfo?>
|
||||||
|
|
||||||
@POST("/neglect/booth/member/info-by-phone")
|
@POST
|
||||||
suspend fun getMemberInfoByPhone(
|
suspend fun getMemberInfoByPhone(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/member/info-by-phone",
|
||||||
@Body request: Map<String, String>
|
@Body request: Map<String, String>
|
||||||
): ApiResponse<NewMemberInfo?>
|
): ApiResponse<NewMemberInfo?>
|
||||||
|
|
||||||
@POST("/neglect/booth/member/discount")
|
@POST
|
||||||
suspend fun getMemberDiscount(
|
suspend fun getMemberDiscount(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/member/discount",
|
||||||
@Body request: Map<String, Long>
|
@Body request: Map<String, Long>
|
||||||
): ApiResponse<String?>
|
): ApiResponse<String?>
|
||||||
|
|
||||||
@POST("/neglect/booth/order/bind-user")
|
@POST
|
||||||
suspend fun bindUserOrder(
|
suspend fun bindUserOrder(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/order/bind-user",
|
||||||
@Body request: BindUserOrderRequest
|
@Body request: BindUserOrderRequest
|
||||||
): ApiResponse<Any?>
|
): ApiResponse<Any?>
|
||||||
|
|
||||||
@POST("/neglect/booth/collect/page")
|
@POST
|
||||||
suspend fun getCollectPage(
|
suspend fun getCollectPage(
|
||||||
@Body request: Map<String, Any>
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/collect/page",
|
||||||
|
@Body request: Map<String, @JvmSuppressWildcards Any>
|
||||||
): ApiResponse<List<CollectedFoodV2>?>
|
): ApiResponse<List<CollectedFoodV2>?>
|
||||||
|
|
||||||
@Multipart
|
@Multipart
|
||||||
@POST("/neglect/booth/collect/upload")
|
@POST
|
||||||
suspend fun uploadCollect(
|
suspend fun uploadCollect(
|
||||||
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/collect/upload",
|
||||||
@PartMap params: HashMap<String, RequestBody>,
|
@PartMap params: HashMap<String, RequestBody>,
|
||||||
@Part foodPics: List<MultipartBody.Part>
|
@Part foodPics: List<MultipartBody.Part>
|
||||||
): ApiResponse<List<String>?>
|
): ApiResponse<List<String>?>
|
||||||
|
|
||||||
@POST("/neglect/booth/collect/delete")
|
@POST
|
||||||
suspend fun deleteCollect(
|
suspend fun deleteCollect(
|
||||||
@Body request: Map<String, Any>
|
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/booth/collect/delete",
|
||||||
|
@Body request: Map<String, @JvmSuppressWildcards Any>
|
||||||
): ApiResponse<Any?>
|
): ApiResponse<Any?>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ import com.sw.dualscreen.model.response.UserNutrition
|
|||||||
import com.sw.dualscreen.utils.Debouncer
|
import com.sw.dualscreen.utils.Debouncer
|
||||||
import com.sw.dualscreen.utils.SPUtil
|
import com.sw.dualscreen.utils.SPUtil
|
||||||
import com.sw.dualscreen.utils.countDownByFlow
|
import com.sw.dualscreen.utils.countDownByFlow
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import com.sw.plate.utils.LightManager
|
import com.sw.plate.utils.LightManager
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import com.sw.plate.utils.arcface.ConfigUtil
|
import com.sw.plate.utils.arcface.ConfigUtil
|
||||||
@@ -73,7 +73,7 @@ import kotlin.math.roundToInt
|
|||||||
class MainScreenPresentation(
|
class MainScreenPresentation(
|
||||||
val activity: MainActivity,
|
val activity: MainActivity,
|
||||||
display: Display,
|
display: Display,
|
||||||
val userViewModel: UserViewModel,
|
val userViewModel: NetViewModelV2,
|
||||||
val recognizeViewModel: RecognizeViewModel,
|
val recognizeViewModel: RecognizeViewModel,
|
||||||
private val onDismissListener: () -> Unit = {}
|
private val onDismissListener: () -> Unit = {}
|
||||||
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
|
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ import com.sw.dualscreen.ext.visible
|
|||||||
import com.sw.dualscreen.model.response.MemberInfo
|
import com.sw.dualscreen.model.response.MemberInfo
|
||||||
import com.sw.dualscreen.model.response.TextBean
|
import com.sw.dualscreen.model.response.TextBean
|
||||||
import com.sw.dualscreen.utils.SpannedUtils
|
import com.sw.dualscreen.utils.SpannedUtils
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import com.sw.plate.utils.ToastUtils
|
import com.sw.plate.utils.ToastUtils
|
||||||
import com.sw.plate.utils.arcface.ConfigUtil
|
import com.sw.plate.utils.arcface.ConfigUtil
|
||||||
import com.sw.plate.utils.arcface.ErrorCodeUtil
|
import com.sw.plate.utils.arcface.ErrorCodeUtil
|
||||||
@@ -56,7 +56,7 @@ import java.util.concurrent.atomic.AtomicBoolean
|
|||||||
class PayPresentation(
|
class PayPresentation(
|
||||||
val activity: PayActivity,
|
val activity: PayActivity,
|
||||||
display: Display,
|
display: Display,
|
||||||
val userViewModel: UserViewModel,
|
val userViewModel: NetViewModelV2,
|
||||||
val recognizeViewModel: RecognizeViewModel,
|
val recognizeViewModel: RecognizeViewModel,
|
||||||
private val onDismissListener: () -> Unit = {}
|
private val onDismissListener: () -> Unit = {}
|
||||||
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
|
) : Presentation(activity, display), ViewTreeObserver.OnGlobalLayoutListener {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|||||||
import okhttp3.MultipartBody
|
import okhttp3.MultipartBody
|
||||||
import okhttp3.RequestBody
|
import okhttp3.RequestBody
|
||||||
import okhttp3.RequestBody.Companion.asRequestBody
|
import okhttp3.RequestBody.Companion.asRequestBody
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
class RemoteRepositoryV2 constructor(
|
class RemoteRepositoryV2 constructor(
|
||||||
@@ -169,22 +170,10 @@ class RemoteRepositoryV2 constructor(
|
|||||||
fileList: List<File>
|
fileList: List<File>
|
||||||
): ApiResponse<List<String>?> {
|
): ApiResponse<List<String>?> {
|
||||||
val params = hashMapOf<String, RequestBody>()
|
val params = hashMapOf<String, RequestBody>()
|
||||||
params["foodId"] = RequestBody.create(
|
params["foodId"] = foodId.toString().toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
"text/plain".toMediaTypeOrNull(),
|
params["foodName"] = foodName.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
foodId.toString()
|
params["version"] = version.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
)
|
params["foodVector"] = foodVector.toRequestBody("text/plain".toMediaTypeOrNull())
|
||||||
params["foodName"] = RequestBody.create(
|
|
||||||
"text/plain".toMediaTypeOrNull(),
|
|
||||||
foodName
|
|
||||||
)
|
|
||||||
params["version"] = RequestBody.create(
|
|
||||||
"text/plain".toMediaTypeOrNull(),
|
|
||||||
version
|
|
||||||
)
|
|
||||||
params["foodVector"] = RequestBody.create(
|
|
||||||
"text/plain".toMediaTypeOrNull(),
|
|
||||||
foodVector
|
|
||||||
)
|
|
||||||
|
|
||||||
val fileParts = mutableListOf<MultipartBody.Part>()
|
val fileParts = mutableListOf<MultipartBody.Part>()
|
||||||
fileList.forEach { file ->
|
fileList.forEach { file ->
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
package com.sw.dualscreen.utils
|
package com.sw.dualscreen.utils
|
||||||
|
|
||||||
import androidx.lifecycle.LifecycleCoroutineScope
|
import androidx.lifecycle.LifecycleCoroutineScope
|
||||||
import com.sw.dualscreen.GlobalData
|
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
|
||||||
import com.sw.dualscreen.model.response.FoodVector
|
|
||||||
import com.sw.dualscreen.objbox.Food
|
import com.sw.dualscreen.objbox.Food
|
||||||
import com.sw.dualscreen.objbox.ObjectBox
|
import com.sw.dualscreen.objbox.ObjectBox
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
object FoodVectorTool {
|
object FoodVectorTool {
|
||||||
@@ -15,19 +14,13 @@ object FoodVectorTool {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 统一入口:分页拉取所有食物向量数据并保存到本地 ObjectBox
|
* 统一入口:分页拉取所有食物向量数据并保存到本地 ObjectBox
|
||||||
*
|
|
||||||
* @param userViewModel 请求 ViewModel
|
|
||||||
* @param lifecycleScope 协程作用域(用于保存操作)
|
|
||||||
* @param successBlock 全部数据拉取并保存成功后回调
|
|
||||||
* @param failureBlock 任意一次请求失败时回调,参数为错误信息
|
|
||||||
*/
|
*/
|
||||||
fun loadAndSaveFoodVector(
|
fun loadAndSaveFoodVector(
|
||||||
userViewModel: UserViewModel,
|
userViewModel: NetViewModelV2,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
successBlock: () -> Unit,
|
successBlock: () -> Unit,
|
||||||
failureBlock: (String) -> Unit
|
failureBlock: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
// 每次调用从第 1 页开始,避免单例状态污染
|
|
||||||
fetchPage(
|
fetchPage(
|
||||||
pageNum = 1,
|
pageNum = 1,
|
||||||
userViewModel = userViewModel,
|
userViewModel = userViewModel,
|
||||||
@@ -37,68 +30,47 @@ object FoodVectorTool {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 递归分页请求(内部方法,外部不感知分页细节)
|
|
||||||
*
|
|
||||||
* @param pageNum 当前页码
|
|
||||||
* @param userViewModel 请求 ViewModel
|
|
||||||
* @param lifecycleScope 协程作用域
|
|
||||||
* @param successBlock 成功回调
|
|
||||||
* @param failureBlock 失败回调
|
|
||||||
*/
|
|
||||||
private fun fetchPage(
|
private fun fetchPage(
|
||||||
pageNum: Int,
|
pageNum: Int,
|
||||||
userViewModel: UserViewModel,
|
userViewModel: NetViewModelV2,
|
||||||
lifecycleScope: LifecycleCoroutineScope,
|
lifecycleScope: LifecycleCoroutineScope,
|
||||||
successBlock: () -> Unit,
|
successBlock: () -> Unit,
|
||||||
failureBlock: (String) -> Unit
|
failureBlock: (String) -> Unit
|
||||||
) {
|
) {
|
||||||
userViewModel.getCollectedFoodVector(
|
userViewModel.getCollectPage(
|
||||||
param = mutableMapOf(
|
pageNum = pageNum.toLong(),
|
||||||
"pageNum" to "$pageNum",
|
pageSize = PAGE_SIZE.toLong(),
|
||||||
"pageSize" to "$PAGE_SIZE",
|
onSuccess = { items ->
|
||||||
"version" to GlobalData.foodModelVersion
|
|
||||||
)
|
|
||||||
) { items ->
|
|
||||||
if (items == null) {
|
|
||||||
failureBlock("未查询到向量数据")
|
|
||||||
return@getCollectedFoodVector
|
|
||||||
}
|
|
||||||
// 第一页为空,视为成功(数据库无数据)
|
|
||||||
if (pageNum == 1 && items.isEmpty()) {
|
if (pageNum == 1 && items.isEmpty()) {
|
||||||
successBlock()
|
successBlock()
|
||||||
return@getCollectedFoodVector
|
return@getCollectPage
|
||||||
}
|
}
|
||||||
// 有数据则保存
|
|
||||||
if (items.isNotEmpty()) {
|
if (items.isNotEmpty()) {
|
||||||
saveFoodVector(lifecycleScope, items)
|
saveFoodVector(lifecycleScope, items)
|
||||||
}
|
}
|
||||||
// 数据量达到一页上限,继续拉取下一页
|
|
||||||
if (items.size >= PAGE_SIZE) {
|
if (items.size >= PAGE_SIZE) {
|
||||||
fetchPage(pageNum + 1, userViewModel, lifecycleScope, successBlock, failureBlock)
|
fetchPage(pageNum + 1, userViewModel, lifecycleScope, successBlock, failureBlock)
|
||||||
} else {
|
} else {
|
||||||
successBlock()
|
successBlock()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onFailure = { failureBlock(it) }
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将向量数据列表保存到 ObjectBox 本地数据库
|
* 将 CollectedFoodV2 列表保存到 ObjectBox 本地数据库
|
||||||
*
|
|
||||||
* @param lifecycleScope 协程作用域
|
|
||||||
* @param list 待保存的食物向量数据列表
|
|
||||||
*/
|
*/
|
||||||
private fun saveFoodVector(lifecycleScope: LifecycleCoroutineScope, list: List<FoodVector>) {
|
private fun saveFoodVector(lifecycleScope: LifecycleCoroutineScope, list: List<CollectedFoodV2>) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val vectorList = list.map { vt ->
|
val vectorList = list.map { item ->
|
||||||
// 解析向量字符串 "[0.1,0.2,...]" 为 FloatArray
|
val foodVector = item.foodVector?.removeSurrounding("[", "]")
|
||||||
val foodVector = vt.foodVector?.removeSurrounding("[", "]")
|
|
||||||
?.split(",")?.map { it.toFloatOrNull() ?: 0.0f }?.toFloatArray()
|
?.split(",")?.map { it.toFloatOrNull() ?: 0.0f }?.toFloatArray()
|
||||||
Food(
|
Food(
|
||||||
collectId = vt.id,
|
collectId = item.foodId,
|
||||||
foodId = vt.foodId,
|
foodId = item.foodId,
|
||||||
foodName = vt.foodName,
|
foodName = item.foodName,
|
||||||
version = vt.version,
|
version = item.version,
|
||||||
foodVector = foodVector
|
foodVector = foodVector
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import com.sw.dualscreen.databinding.BottomSheetDialogBinding
|
|||||||
import com.sw.dualscreen.model.response.FoodInfo
|
import com.sw.dualscreen.model.response.FoodInfo
|
||||||
import com.sw.dualscreen.utils.Debouncer
|
import com.sw.dualscreen.utils.Debouncer
|
||||||
import com.sw.dualscreen.utils.KeyboardUtils
|
import com.sw.dualscreen.utils.KeyboardUtils
|
||||||
import com.sw.dualscreen.viewmodel.UserViewModel
|
import com.sw.dualscreen.viewmodel.NetViewModelV2
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import androidx.core.graphics.drawable.toDrawable
|
import androidx.core.graphics.drawable.toDrawable
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ import androidx.core.graphics.drawable.toDrawable
|
|||||||
*/
|
*/
|
||||||
@SuppressLint("NotifyDataSetChanged")
|
@SuppressLint("NotifyDataSetChanged")
|
||||||
class CustomBottomSheetDialog(
|
class CustomBottomSheetDialog(
|
||||||
val viewModel: UserViewModel,
|
val viewModel: NetViewModelV2,
|
||||||
val itemClickCallback: (FoodInfo) -> Unit
|
val itemClickCallback: (FoodInfo) -> Unit
|
||||||
) : BottomSheetDialogFragment() {
|
) : BottomSheetDialogFragment() {
|
||||||
private lateinit var binding: BottomSheetDialogBinding
|
private lateinit var binding: BottomSheetDialogBinding
|
||||||
@@ -87,7 +87,7 @@ class CustomBottomSheetDialog(
|
|||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
viewModel: UserViewModel,
|
viewModel: NetViewModelV2,
|
||||||
itemClickCallback: (FoodInfo) -> Unit
|
itemClickCallback: (FoodInfo) -> Unit
|
||||||
): CustomBottomSheetDialog {
|
): CustomBottomSheetDialog {
|
||||||
return CustomBottomSheetDialog(viewModel, itemClickCallback)
|
return CustomBottomSheetDialog(viewModel, itemClickCallback)
|
||||||
|
|||||||
@@ -1,9 +1,18 @@
|
|||||||
package com.sw.dualscreen.viewmodel
|
package com.sw.dualscreen.viewmodel
|
||||||
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.arcsoft.face.ErrorInfo
|
||||||
|
import com.sw.dualscreen.GlobalData
|
||||||
|
import com.sw.dualscreen.model.request.UserNutritionParam
|
||||||
import com.sw.dualscreen.model.request.v2.BindUserOrderRequest
|
import com.sw.dualscreen.model.request.v2.BindUserOrderRequest
|
||||||
import com.sw.dualscreen.model.request.v2.PlaceOrderRequest
|
import com.sw.dualscreen.model.request.v2.PlaceOrderRequest
|
||||||
|
import com.sw.dualscreen.model.response.ApiResponse
|
||||||
|
import com.sw.dualscreen.model.response.DinnerType
|
||||||
|
import com.sw.dualscreen.model.response.FoodInfo
|
||||||
|
import com.sw.dualscreen.model.response.FoodOrder
|
||||||
|
import com.sw.dualscreen.model.response.FoodOrderModel
|
||||||
import com.sw.dualscreen.model.response.FoodSearchReq
|
import com.sw.dualscreen.model.response.FoodSearchReq
|
||||||
|
import com.sw.dualscreen.model.response.MemberInfo
|
||||||
import com.sw.dualscreen.model.response.UserNutrition
|
import com.sw.dualscreen.model.response.UserNutrition
|
||||||
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
|
import com.sw.dualscreen.model.response.v2.CollectedFoodV2
|
||||||
import com.sw.dualscreen.model.response.v2.FaceVO
|
import com.sw.dualscreen.model.response.v2.FaceVO
|
||||||
@@ -11,10 +20,20 @@ import com.sw.dualscreen.model.response.v2.NewFoodInfo
|
|||||||
import com.sw.dualscreen.model.response.v2.NewMemberInfo
|
import com.sw.dualscreen.model.response.v2.NewMemberInfo
|
||||||
import com.sw.dualscreen.model.response.v2.SettlementOrder
|
import com.sw.dualscreen.model.response.v2.SettlementOrder
|
||||||
import com.sw.dualscreen.network.ApiClient
|
import com.sw.dualscreen.network.ApiClient
|
||||||
|
import com.sw.dualscreen.utils.FileUtil
|
||||||
|
import com.sw.dualscreen.utils.SpTool
|
||||||
|
import com.sw.plate.App
|
||||||
|
import com.sw.plate.utils.Base64
|
||||||
|
import com.sw.plate.utils.ToastUtils
|
||||||
|
import com.sw.plate.utils.arcface.FaceApi
|
||||||
|
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class NetViewModelV2 : BaseViewModel() {
|
class NetViewModelV2 : BaseViewModel() {
|
||||||
|
|
||||||
@@ -23,6 +42,16 @@ class NetViewModelV2 : BaseViewModel() {
|
|||||||
const val PAGE_SIZE = 100L
|
const val PAGE_SIZE = 100L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val faceApi: FaceApi = FaceApi()
|
||||||
|
|
||||||
|
/** 人脸加载完成 */
|
||||||
|
private val _loadFaceResult = MutableStateFlow<Boolean>(false)
|
||||||
|
val loadFaceResult: StateFlow<Boolean> = _loadFaceResult
|
||||||
|
|
||||||
|
/** 饭点类型 早餐/午餐/晚餐 */
|
||||||
|
private val _dinnerTypeInfo = MutableStateFlow<DinnerType?>(null)
|
||||||
|
val dinnerTypeInfo: StateFlow<DinnerType?> = _dinnerTypeInfo
|
||||||
|
|
||||||
fun getDeviceConfig(
|
fun getDeviceConfig(
|
||||||
onSuccess: (config: com.sw.dualscreen.model.response.DeviceConfig?) -> Unit,
|
onSuccess: (config: com.sw.dualscreen.model.response.DeviceConfig?) -> Unit,
|
||||||
onFailure: (message: String) -> Unit
|
onFailure: (message: String) -> Unit
|
||||||
@@ -42,49 +71,82 @@ class NetViewModelV2 : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取人脸全量数据(递归分页 + FaceApi 集成) */
|
||||||
fun getFacePage(
|
fun getFacePage(
|
||||||
pageNum: Long = 1L,
|
pageNum: Long = 1L,
|
||||||
pageSize: Long = PAGE_SIZE,
|
pageSize: Long = PAGE_SIZE,
|
||||||
onSuccess: (list: List<FaceVO>, hasNext: Boolean) -> Unit,
|
onSuccess: () -> Unit = {},
|
||||||
onFailure: (message: String) -> Unit
|
onFailure: (String) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
var currentPageNum = pageNum
|
||||||
|
Timber.tag(TAG).d("getFacePage index = $currentPageNum")
|
||||||
launch {
|
launch {
|
||||||
try {
|
_loadFaceResult.value = false
|
||||||
val response = ApiClient.repositoryV2.getFacePage(pageNum, pageSize)
|
SpTool.lastFaceTimestamp = 0
|
||||||
if (parseResponse(response)) {
|
FileUtil.saveLog("获取人脸全量数据开始,重置时间戳为0")
|
||||||
val list = response.data ?: emptyList()
|
val response = ApiClient.repositoryV2.getFacePage(currentPageNum, pageSize)
|
||||||
val hasNext = list.size >= pageSize
|
if (!parseResponse(response)) {
|
||||||
onSuccess(list, hasNext)
|
onFailure("获取人脸数据失败,${response.msg}(${response.code})")
|
||||||
} else {
|
return@launch
|
||||||
onFailure(response.msg ?: "获取人脸数据失败")
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
withContext(Dispatchers.Default) {
|
||||||
Timber.e(e, "获取全量人脸数据异常")
|
val list = response.data ?: emptyList()
|
||||||
onFailure("网络异常: ${e.message}")
|
if (pageNum == 1L && list.isEmpty()) {
|
||||||
|
onFailure("查询人脸数据为空")
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
val faceEntity = list.map { vo ->
|
||||||
|
FaceEntity(vo.userId, null, Base64.decode(vo.faceFeature)).also {
|
||||||
|
it.userType = if (vo.member == true) "1" else "2"
|
||||||
|
it.cardNo = vo.cardNo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
faceApi.updateFaceData(currentPageNum.toInt(), faceEntity)
|
||||||
|
if (list.size >= pageSize) {
|
||||||
|
lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L
|
||||||
|
currentPageNum++
|
||||||
|
getFacePage(currentPageNum, pageSize, onSuccess, onFailure)
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L
|
||||||
|
}
|
||||||
|
SpTool.lastFaceTimestamp = lastFaceTimestamp
|
||||||
|
FileUtil.saveLog("获取人脸[全量]数据结束,时间戳为:$lastFaceTimestamp")
|
||||||
|
_loadFaceResult.value = true
|
||||||
|
onSuccess()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取人脸增量数据(FaceApi 集成) */
|
||||||
fun getFaceIncrement(
|
fun getFaceIncrement(
|
||||||
pageNum: Long = 1L,
|
pageNum: Long = 1L,
|
||||||
pageSize: Long = PAGE_SIZE,
|
pageSize: Long = PAGE_SIZE,
|
||||||
timestamp: Long,
|
timestamp: Long,
|
||||||
onSuccess: (list: List<FaceVO>, hasNext: Boolean) -> Unit,
|
onAllQueryFinished: () -> Unit,
|
||||||
onFailure: (message: String) -> Unit
|
onPageQueryFinished: (List<FaceVO>) -> Unit,
|
||||||
|
onFailure: (String) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
Timber.tag(TAG).d("getFaceIncrement timestamp=$timestamp")
|
||||||
launch {
|
launch {
|
||||||
try {
|
|
||||||
val response = ApiClient.repositoryV2.getFaceIncrement(pageNum, pageSize, timestamp)
|
val response = ApiClient.repositoryV2.getFaceIncrement(pageNum, pageSize, timestamp)
|
||||||
if (parseResponse(response)) {
|
if (!parseResponse(response)) {
|
||||||
val list = response.data ?: emptyList()
|
|
||||||
val hasNext = list.size >= pageSize
|
|
||||||
onSuccess(list, hasNext)
|
|
||||||
} else {
|
|
||||||
onFailure(response.msg ?: "获取增量人脸数据失败")
|
onFailure(response.msg ?: "获取增量人脸数据失败")
|
||||||
|
return@launch
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
withContext(Dispatchers.Default) {
|
||||||
Timber.e(e, "获取增量人脸数据异常")
|
val list = response.data ?: emptyList()
|
||||||
onFailure("网络异常: ${e.message}")
|
if (pageNum == 1L && list.isEmpty()) {
|
||||||
|
return@withContext
|
||||||
|
}
|
||||||
|
onPageQueryFinished(list)
|
||||||
|
if (list.isNotEmpty()) {
|
||||||
|
lastFaceTimestamp = list.last().faceUpdateTimestamp ?: 0L
|
||||||
|
}
|
||||||
|
SpTool.lastFaceTimestamp = lastFaceTimestamp
|
||||||
|
FileUtil.saveLog("获取人脸[增量]数据结束,时间戳为:$lastFaceTimestamp")
|
||||||
|
onAllQueryFinished()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,7 +252,7 @@ class NetViewModelV2 : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getMemberInfoByPhone(
|
fun getMemberInfoByPhoneV2(
|
||||||
phone: String,
|
phone: String,
|
||||||
password: String = "",
|
password: String = "",
|
||||||
onSuccess: (memberInfo: NewMemberInfo?) -> Unit,
|
onSuccess: (memberInfo: NewMemberInfo?) -> Unit,
|
||||||
@@ -275,72 +337,326 @@ class NetViewModelV2 : BaseViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun uploadCollect(
|
/** 上传采集图片(suspend,直接返回 picUrls 列表) */
|
||||||
|
suspend fun uploadCollect(
|
||||||
foodId: Long,
|
foodId: Long,
|
||||||
foodName: String,
|
foodName: String,
|
||||||
version: String,
|
version: String,
|
||||||
foodVector: String,
|
foodVector: String,
|
||||||
fileList: List<okhttp3.MultipartBody.Part>,
|
fileList: List<File>
|
||||||
onSuccess: (picUrls: List<String>) -> Unit,
|
): List<String>? {
|
||||||
onFailure: (message: String) -> Unit
|
Timber.tag(TAG).d("uploadCollect foodId=$foodId, foodName=$foodName")
|
||||||
) {
|
val fileListNotNull = fileList.filter { it.exists() }
|
||||||
launchWithLoading {
|
if (fileListNotNull.isEmpty()) {
|
||||||
try {
|
return null
|
||||||
val params = hashMapOf<String, okhttp3.RequestBody>()
|
}
|
||||||
params["foodId"] = okhttp3.RequestBody.create(
|
|
||||||
okhttp3.MediaType.parse("text/plain"),
|
|
||||||
foodId.toString()
|
|
||||||
)
|
|
||||||
params["foodName"] = okhttp3.RequestBody.create(
|
|
||||||
okhttp3.MediaType.parse("text/plain"),
|
|
||||||
foodName
|
|
||||||
)
|
|
||||||
params["version"] = okhttp3.RequestBody.create(
|
|
||||||
okhttp3.MediaType.parse("text/plain"),
|
|
||||||
version
|
|
||||||
)
|
|
||||||
params["foodVector"] = okhttp3.RequestBody.create(
|
|
||||||
okhttp3.MediaType.parse("text/plain"),
|
|
||||||
foodVector
|
|
||||||
)
|
|
||||||
|
|
||||||
val response = ApiClient.repositoryV2.uploadCollect(
|
val response = ApiClient.repositoryV2.uploadCollect(
|
||||||
foodId, foodName, version, foodVector,
|
foodId, foodName, version, foodVector, fileListNotNull
|
||||||
fileList.map { it.body().contentType()?.let { ct ->
|
|
||||||
okhttp3.RequestBody.create(ct, (it.body() as okhttp3.ResponseBody).bytes())
|
|
||||||
} ?: it.body() } as List<File>
|
|
||||||
)
|
)
|
||||||
|
if (!parseResponse(response)) {
|
||||||
if (parseResponse(response)) {
|
return null
|
||||||
onSuccess(response.data ?: emptyList())
|
|
||||||
} else {
|
|
||||||
onFailure(response.msg ?: "上传采集数据失败")
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "上传采集数据异常")
|
|
||||||
onFailure("网络异常: ${e.message}")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return response.data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 删除采集菜品 */
|
||||||
fun deleteCollect(
|
fun deleteCollect(
|
||||||
foodId: Long,
|
foodId: String?,
|
||||||
version: String,
|
version: String?,
|
||||||
onSuccess: () -> Unit,
|
block: (Boolean) -> Unit
|
||||||
onFailure: (message: String) -> Unit
|
|
||||||
) {
|
) {
|
||||||
|
Timber.tag(TAG).d("deleteCollect foodId=$foodId, version=$version")
|
||||||
|
val id = foodId?.toLongOrNull() ?: run {
|
||||||
|
block(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
launchWithLoading {
|
launchWithLoading {
|
||||||
try {
|
try {
|
||||||
val response = ApiClient.repositoryV2.deleteCollect(foodId, version)
|
val response = ApiClient.repositoryV2.deleteCollect(id, version ?: "")
|
||||||
if (parseResponse(response)) {
|
if (parseResponse(response)) {
|
||||||
onSuccess()
|
block(true)
|
||||||
} else {
|
} else {
|
||||||
onFailure(response.msg ?: "删除采集菜品失败")
|
block(false)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "删除采集菜品异常")
|
Timber.e(e, "删除采集菜品异常")
|
||||||
|
block(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 以下方法暂用 V1 API 实现(待 V2 接口补充) ==========
|
||||||
|
|
||||||
|
/** 激活虹软人脸识别引擎 */
|
||||||
|
fun activeEngine() {
|
||||||
|
Timber.tag(TAG).d("activeEngine")
|
||||||
|
faceApi.activeEngine(
|
||||||
|
App.getContext(),
|
||||||
|
GlobalData.appId,
|
||||||
|
GlobalData.sdkKey,
|
||||||
|
GlobalData.activeKey,
|
||||||
|
object : FaceApi.ActiveCallback {
|
||||||
|
override fun onSuccess(activeCode: Int) {
|
||||||
|
Timber.tag(TAG).d("activeEngine activeCode = $activeCode")
|
||||||
|
viewModelScope.launch(Dispatchers.Main) {
|
||||||
|
when (activeCode) {
|
||||||
|
ErrorInfo.MOK -> {
|
||||||
|
ToastUtils.showToast("激活引擎成功")
|
||||||
|
}
|
||||||
|
ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> {
|
||||||
|
// 引擎已激活,无需再次激活
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
ToastUtils.showToast("激活引擎失败($activeCode)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFail(e: Exception?) {
|
||||||
|
viewModelScope.launch(Dispatchers.Main) {
|
||||||
|
ToastUtils.showToast("激活引擎异常,${e?.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取支付二维码 */
|
||||||
|
fun getQrCodeImg(
|
||||||
|
orderId: String,
|
||||||
|
userId: String? = null,
|
||||||
|
totalFee: String? = null,
|
||||||
|
block: (String?) -> Unit
|
||||||
|
) {
|
||||||
|
Timber.tag(TAG).d("getQrCodeImg orderId = $orderId, userId = $userId")
|
||||||
|
launch {
|
||||||
|
val response = repository.getQrCodeImg(
|
||||||
|
orderNo = orderId, memberId = userId, totalFee = totalFee
|
||||||
|
)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data)
|
||||||
|
} else {
|
||||||
|
block(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 扫码支付 */
|
||||||
|
fun qrCodePay(
|
||||||
|
authCode: String,
|
||||||
|
orderNo: String,
|
||||||
|
memberId: String?,
|
||||||
|
block: (Boolean, String?) -> Unit
|
||||||
|
) {
|
||||||
|
launch {
|
||||||
|
val response = repository.qrCodePay(authCode, orderNo, memberId)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(true, response.data)
|
||||||
|
} else {
|
||||||
|
block(false, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 现金支付 */
|
||||||
|
fun cashPay(param: HashMap<String, String>, block: (Boolean) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("cashPay")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.cashPay(param)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data ?: false)
|
||||||
|
} else {
|
||||||
|
block(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 会员支付 */
|
||||||
|
fun memberPay(param: HashMap<String, String?>, block: (Boolean) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("memberPay")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.memberPay(param)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(true)
|
||||||
|
} else {
|
||||||
|
block(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 查询订单支付状态 */
|
||||||
|
suspend fun queryOrderState(orderId: String, block: (Boolean) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("queryOrderState")
|
||||||
|
val response = repository.queryOrderState(orderNo = orderId)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data == "1")
|
||||||
|
} else {
|
||||||
|
block(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取饭点类型 */
|
||||||
|
fun getDinnerType(
|
||||||
|
onSuccess: (DinnerType?) -> Unit = {},
|
||||||
|
onFailure: (message: String) -> Unit = {}
|
||||||
|
) {
|
||||||
|
_dinnerTypeInfo.value = null
|
||||||
|
launch {
|
||||||
|
try {
|
||||||
|
val response = repository.getDinnerType()
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
_dinnerTypeInfo.value = response.data
|
||||||
|
onSuccess(response.data)
|
||||||
|
} else {
|
||||||
|
onFailure(response.msg ?: "获取饭点类型失败")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "获取饭点类型异常")
|
||||||
onFailure("网络异常: ${e.message}")
|
onFailure("网络异常: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 提交就餐营养数据 */
|
||||||
|
fun postUserNutritionData(
|
||||||
|
param: List<UserNutritionParam>,
|
||||||
|
onSuccess: () -> Unit = {},
|
||||||
|
onFailure: (message: String) -> Unit = {}
|
||||||
|
) {
|
||||||
|
launch {
|
||||||
|
try {
|
||||||
|
val response = repository.postUserNutritionData(param)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
onSuccess()
|
||||||
|
} else {
|
||||||
|
onFailure(response.msg ?: "提交营养数据失败")
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Timber.e(e, "提交营养数据异常")
|
||||||
|
onFailure("网络异常: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== V1 兼容包装方法(暂用 V1 API,后续逐步迁移至 V2) ==========
|
||||||
|
|
||||||
|
private var lastFaceTimestamp = 0L
|
||||||
|
|
||||||
|
/** 搜索食物 */
|
||||||
|
fun searchByFoodName(foodName: String, action: (List<FoodInfo>) -> Unit = {}) {
|
||||||
|
Timber.tag(TAG).d("searchByFoodName foodName = $foodName")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.getRestInfoFoodsByType(foodName = foodName)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
action(response.data ?: emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取用户就餐营养数据 */
|
||||||
|
fun getUserNutritionData(
|
||||||
|
userId: String,
|
||||||
|
block: (UserNutrition?) -> Unit
|
||||||
|
) {
|
||||||
|
Timber.tag(TAG).d("getUserNutritionData userId = $userId")
|
||||||
|
launch {
|
||||||
|
val response = repository.getUserNutritionData(userId = userId)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data)
|
||||||
|
} else {
|
||||||
|
block(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索菜品信息 */
|
||||||
|
fun getFoodInfo(foodName: String, action: (List<FoodInfo>) -> Unit = {}) {
|
||||||
|
Timber.tag(TAG).d("getFoodInfo")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.getFoodInfo(foodName = foodName)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
action(response.data ?: emptyList())
|
||||||
|
} else {
|
||||||
|
action(emptyList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 创建订单 */
|
||||||
|
fun createOrder(order: FoodOrder, block: (String) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("createOrder")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.createOrder(order)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data.toString())
|
||||||
|
} else {
|
||||||
|
block("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取订单列表 */
|
||||||
|
fun getFoodOrderList(userId: String, block: (FoodOrderModel?) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("getFoodOrderList")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.getFoodOrderList(userId)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data)
|
||||||
|
} else {
|
||||||
|
block(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据 ID 查询会员信息 */
|
||||||
|
fun getMemberInfoById(memberId: String, block: (MemberInfo?) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("getMemberInfo")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.getMemberInfoById(memberId)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data)
|
||||||
|
} else {
|
||||||
|
block(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据手机号查询会员信息 */
|
||||||
|
fun getMemberInfoByPhone(phone: String, key: String, block: (MemberInfo?) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("getMemberInfoByPhone")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.getMemberInfoByPhone(phone, key)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data)
|
||||||
|
} else {
|
||||||
|
block(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 绑定订单 */
|
||||||
|
fun bindOrder(userId: String, orderId: String, mode: Int, block: (Boolean) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("bindOrder")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.bindOrder(userId = userId, orderId = orderId, mode = mode)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(true)
|
||||||
|
} else {
|
||||||
|
block(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取会员折扣 */
|
||||||
|
fun getMemberDiscount(userId: String, block: (Double?) -> Unit) {
|
||||||
|
Timber.tag(TAG).d("getMemberDiscount")
|
||||||
|
launchWithLoading {
|
||||||
|
val response = repository.getMemberDiscount(userId)
|
||||||
|
if (parseResponse(response)) {
|
||||||
|
block(response.data)
|
||||||
|
} else {
|
||||||
|
block(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user