refactor(login): 优化出盘流程并增强错误处理

- 抽取出盘成功与失败的统一处理方法,简化代码逻辑
- 新增新用户注册采集成功后立即出盘功能,避免与识别成功逻辑冲突
- 出盘成功后取消识别倒计时并控制出盘开关,防止重复出盘
- 出盘失败时统一提示错误并跳转回初始页
- 采集成功后立即异步出盘,确保上传失败时仍能出盘
- 调整识别失败计数重置逻辑,避免无人脸短暂丢帧导致计数异常
- 在人脸离开时重置识别失败计数,保证下次重新累计
- 网络请求增加异常捕获,防止因异常导致回调阻塞无反馈
This commit is contained in:
mazengfei
2026-07-08 18:30:14 +08:00
parent 9a8ac83d33
commit 5b29c99b34
2 changed files with 79 additions and 28 deletions
@@ -417,28 +417,67 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
PlcHelper.getInstance(this@LoginByFaceActivity)
.outPlate(object : PlcCallback<String> {
override fun onSuccess(result: String) {
loadLogInfo("collectFace,openPlate: 出盘完成,result=$result,开始查询剩余餐盘")
onOutPlateSuccess(result, RECOGNIZE_SUCCESS)
}
override fun onError(message: String) {
onOutPlateError(message)
}
})
}
/**
* 新用户注册采集成功后立即出盘(与上传后台并行,互不阻塞)。
* 故意不经过 recognizeSuccess:避免使用临时 userId、避免与识别成功的防抖语义混淆。
*/
private fun outPlateForRegister() {
loadLogInfo("collectFace,outPlateForRegister: 新用户采集成功,立即出盘,plateEnable=$plateEnable")
if (plateEnable.not()) return
PlcHelper.getInstance(this@LoginByFaceActivity)
.outPlate(object : PlcCallback<String> {
override fun onSuccess(result: String) {
loadLogInfo("collectFace,outPlateForRegister: 出盘完成,result=$result")
onOutPlateSuccess(result, COLLECT_SUCCESS)
}
override fun onError(message: String) {
onOutPlateError(message)
}
})
}
/**
* 出盘成功后的统一处理:
* 1. 置 plateEnable=false、取消识别倒计时,防止重复出盘;
* 2. result 为 "false" 表示出盘后已无餐盘,播放无盘提示音并跳转;
* 3. 否则播放提示音并查询剩余餐盘状态。
*
* @param result PLC 返回结果("false" 表示出盘后无餐盘)
* @param successSound 出盘成功播放的提示音 key(识别成功 / 采集成功)
*/
private fun onOutPlateSuccess(result: String, successSound: String) {
plateEnable = false
recognizeCountDownUtil.cancelCountDown()
// result 为 false 时表示出盘后无餐盘,直接播放无盘提示音
if (result.equals("false", ignoreCase = true)) {
loadLogInfo("collectFace,openPlate: result=false,餐盘已空")
loadLogInfo("collectFace,onOutPlateSuccess: result=false,餐盘已空")
SoundPoolUtil.getInstance().play(NO_PLATE_REMIND, 0)
goInitActivity()
return
}
loadLogInfo("collectFace,openPlate: 出盘完成,result=$result,开始查询剩余餐盘")
// result 为 true,出盘成功,再查询是否还有剩余餐盘
SoundPoolUtil.getInstance().play(RECOGNIZE_SUCCESS, 0)
SoundPoolUtil.getInstance().play(successSound, 0)
checkRemainingPlate()
}
override fun onError(message: String) {
/**
* 出盘失败后的统一处理:提示失败并跳转回初始页
*/
private fun onOutPlateError(message: String) {
ToastUtils.showToast("出盘失败,message=$message")
loadLogInfo("collectFace,openPlate: 出盘失败,message=$message")
loadLogInfo("collectFace,onOutPlateError: 出盘失败,message=$message")
goInitActivity()
}
})
}
/**
* 出盘成功后查询剩余餐盘状态,无盘则播放提示音
@@ -617,7 +656,9 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
userFaceInfo = null
binding.btnCollect.tag = ""
binding.tvSimilarValue.text = ""
retryFailCount = 0
// 注意:此处不再重置 retryFailCount。
// 人脸检测会抖动(短暂丢帧),若每次无人脸就清零,"尝试几次失败"将无法累计。
// 失败计数仅在识别成功、会话重启、确认用户离开时清零。
// 人脸离开时,发送空帧清除 ViewModel 内部识别缓存,防止再次进入时使用旧数据
recognizeViewModel.onPreviewFrame(emptyFrame, true)
if (binding.llCollectButton.isVisible) {
@@ -922,6 +963,9 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
//block(faceData, headBmp)
isCollecting.set(false)
isCollectFace = false
// 采集成功后立即出盘,与上传后台并行执行,确保上传失败时仍能吐盘
outPlateForRegister()
// 异步上传后台(上传成功后才入库),不阻塞出盘
sendFaceData(faceData, headBmp)
}
@@ -950,9 +994,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
recognizeViewModel.refreshFaceList()
loadLogInfo("collectFace,saveFaceInfo: ----------11,刷新人脸数据-----------")
runOnUiThread {
//采集完成,不再识别,直接提示成功
//采集完成,停止识别(出盘已在采集成功时由 outPlateForRegister 触发,此处仅负责入库)
isRecognition = false
recognizeSuccess("${System.currentTimeMillis()}")
}
loadLogInfo("---------------------------------")
collectCount = 0
@@ -984,6 +1027,8 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
loadLogInfo("leftCountDown.onFinish-人脸未离开,不跳转")
return@startCountDown
}
// 确认用户已离开画面,重置识别失败计数,下次进入重新累计
retryFailCount = 0
goInitActivity()
})
}
@@ -249,6 +249,7 @@ class NetViewModelV2 : ViewModel() {
*/
fun addUserFace(faceData: String, imageFile: File, block: (Boolean, UserFaceModelV2?) -> Unit) {
viewModelScope.launch {
try {
val imageResp = repository.uploadImage(imageFile)
val imageUrl = imageResp.data ?: ""
if (imageUrl.isBlank()) {
@@ -261,6 +262,11 @@ class NetViewModelV2 : ViewModel() {
return@launch
}
block(true, faceResp.data)
} catch (e: Exception) {
// 网络或解析异常时同样回调失败,避免 block 永不触发导致上层无反馈
e.printStackTrace()
block(false, null)
}
}
}