From 9a8ac83d333ea05ebbfdd38b0d179c303d179b9c Mon Sep 17 00:00:00 2001 From: mazengfei <331023091@qq.com> Date: Tue, 7 Jul 2026 17:33:04 +0800 Subject: [PATCH] =?UTF-8?q?feat(face):=20=E6=96=B0=E5=A2=9E=E4=BA=BA?= =?UTF-8?q?=E8=84=B8=E5=BA=93=E5=B1=95=E7=A4=BA=E4=B8=8E=E5=A2=9E=E9=87=8F?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在PLC设置界面新增人脸库统计模块,显示人脸总数并支持刷新 - 人脸总数数据从本地数据库异步查询并更新界面 - 修改API接口调用,采用POST请求体方式提交人脸数据(包含url和特征字符串) - 在网络调用中实现人脸数据的增量分页获取,递归拉取所有数据页 - 维护人脸数据的最大时间戳,确保增量更新只处理最新数据 - 优化人脸数据缓存逻辑,第一页重置数据并同步时间戳 - 添加FeatureBody数据类封装人脸特征请求体参数 - 修正相关仓库层方法以适配新的API接口参数和返回结构 --- .../activity/PlcSettingActivity.kt | 26 ++++++++++ .../platecabinet/model/request/FeatureBody.kt | 6 +++ .../platecabinet/network/api/ApiServiceV2.kt | 5 +- .../repository/RemoteRepositoryV2.kt | 22 ++++---- .../platecabinet/viewmodel/NetViewModelV2.kt | 50 ++++++++++++++++--- .../main/res/layout/activity_plc_setting.xml | 48 ++++++++++++++++++ 6 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/com/sw/platecabinet/model/request/FeatureBody.kt diff --git a/app/src/main/java/com/sw/platecabinet/activity/PlcSettingActivity.kt b/app/src/main/java/com/sw/platecabinet/activity/PlcSettingActivity.kt index 3867e35..52f313c 100644 --- a/app/src/main/java/com/sw/platecabinet/activity/PlcSettingActivity.kt +++ b/app/src/main/java/com/sw/platecabinet/activity/PlcSettingActivity.kt @@ -5,6 +5,7 @@ import android.content.Intent import android.view.inputmethod.InputMethodManager import androidx.lifecycle.lifecycleScope import com.sw.plate.utils.ToastUtils +import com.sw.plate.utils.arcface.facedb.FaceDatabase import com.sw.plate.utils.arcface.faceserver.FaceServer import com.sw.platecabinet.databinding.ActivityPlcSettingBinding import com.sw.platecabinet.databinding.ItemTitleTimeBinding @@ -60,6 +61,7 @@ class PlcSettingActivity : BaseActivity() { initAlarmButton() initParamButtons() initStatusButton() + initFaceCount() binding.btnSwitchEnv.setOnClickListener { EnvSwitchDialog(context = this, onEnvChanged = { showWaitingDialog("重置人脸数据中……") @@ -334,6 +336,30 @@ class PlcSettingActivity : BaseActivity() { """.trimIndent() } + // ==================== 人脸库统计 ==================== + + /** + * 初始化人脸库统计区:页面加载时查询并显示人脸总数,绑定刷新按钮 + */ + private fun initFaceCount() { + refreshFaceCount() + binding.btnRefreshFaceCount.clickWithDebounce { + refreshFaceCount() + } + } + + /** + * 从数据库查询人脸总数并更新 UI + */ + private fun refreshFaceCount() { + lifecycleScope.launch { + val count = withContext(Dispatchers.IO) { + FaceDatabase.getInstance(this@PlcSettingActivity).faceDao().faceCount + } + binding.tvFaceCount.text = "人脸总数:${count}" + } + } + // ==================== 生命周期 ==================== /** diff --git a/app/src/main/java/com/sw/platecabinet/model/request/FeatureBody.kt b/app/src/main/java/com/sw/platecabinet/model/request/FeatureBody.kt new file mode 100644 index 0000000..e11c93c --- /dev/null +++ b/app/src/main/java/com/sw/platecabinet/model/request/FeatureBody.kt @@ -0,0 +1,6 @@ +package com.sw.platecabinet.model.request + +data class FeatureBody( + val url:String, + val featureChar:String +) \ No newline at end of file diff --git a/app/src/main/java/com/sw/platecabinet/network/api/ApiServiceV2.kt b/app/src/main/java/com/sw/platecabinet/network/api/ApiServiceV2.kt index a61a27b..9e9b956 100644 --- a/app/src/main/java/com/sw/platecabinet/network/api/ApiServiceV2.kt +++ b/app/src/main/java/com/sw/platecabinet/network/api/ApiServiceV2.kt @@ -2,6 +2,7 @@ package com.sw.platecabinet.network.api import com.sw.platecabinet.GlobalData import com.sw.platecabinet.model.DeviceConfigV2 +import com.sw.platecabinet.model.request.FeatureBody import com.sw.platecabinet.model.response.ApiResponse import com.sw.platecabinet.model.response.UserFaceModel import com.sw.platecabinet.model.response.UserFaceModelV2 @@ -46,11 +47,9 @@ interface ApiServiceV2 { * 添加人脸数据 */ @POST - @FormUrlEncoded suspend fun addUserFace( @Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/user/add-by-face", - @Field("featureChar") faceData: String, - @Field("url") imageUr: String + @Body faceData: FeatureBody ): ApiResponse /** diff --git a/app/src/main/java/com/sw/platecabinet/repository/RemoteRepositoryV2.kt b/app/src/main/java/com/sw/platecabinet/repository/RemoteRepositoryV2.kt index 49f339e..9dc58a4 100644 --- a/app/src/main/java/com/sw/platecabinet/repository/RemoteRepositoryV2.kt +++ b/app/src/main/java/com/sw/platecabinet/repository/RemoteRepositoryV2.kt @@ -1,6 +1,7 @@ package com.sw.platecabinet.repository import com.sw.platecabinet.model.DeviceConfigV2 +import com.sw.platecabinet.model.request.FeatureBody import com.sw.platecabinet.model.response.ApiResponse import com.sw.platecabinet.model.response.UserFaceModel import com.sw.platecabinet.model.response.UserFaceModelV2 @@ -25,8 +26,7 @@ class RemoteRepositoryV2 constructor( return safeApiCall { apiService.getUserFaceCache( param = mapOf( - "pageNum" to pageNum, - "pageSize" to pageSize + "pageNum" to pageNum, "pageSize" to pageSize ) ) } @@ -34,16 +34,12 @@ class RemoteRepositoryV2 constructor( suspend fun getFaceIncrementList( - pageNum: Long, - pageSize: Long = 100L, - timestamp: Long + pageNum: Long, pageSize: Long = 100L, timestamp: Long ): ApiResponse?> { return safeApiCall { apiService.getFaceIncrementList( param = mapOf( - "pageNum" to pageNum, - "pageSize" to pageSize, - "timestamp" to timestamp + "pageNum" to pageNum, "pageSize" to pageSize, "timestamp" to timestamp ) ) } @@ -53,8 +49,14 @@ class RemoteRepositoryV2 constructor( return safeApiCall { apiService.getDeviceConfig() } } - suspend fun addUserFace(faceData: String, imageUr: String): ApiResponse { - return safeApiCall { apiService.addUserFace(faceData = faceData, imageUr = imageUr) } + suspend fun addUserFace(faceData: String, imageUrl: String): ApiResponse { + return safeApiCall { + apiService.addUserFace( + faceData = FeatureBody( + url = imageUrl, featureChar = faceData + ) + ) + } } suspend fun uploadImage(file: File): ApiResponse { diff --git a/app/src/main/java/com/sw/platecabinet/viewmodel/NetViewModelV2.kt b/app/src/main/java/com/sw/platecabinet/viewmodel/NetViewModelV2.kt index 0d6d4b8..261d2e2 100644 --- a/app/src/main/java/com/sw/platecabinet/viewmodel/NetViewModelV2.kt +++ b/app/src/main/java/com/sw/platecabinet/viewmodel/NetViewModelV2.kt @@ -82,12 +82,17 @@ class NetViewModelV2 : ViewModel() { } withContext(Dispatchers.Default) { if (currentPageNo == 1) { - //第一页数据删除本地数据库 + //第一页数据删除本地数据库,同时重置时间戳 faceApi.clearFaceData() + faceTimestamp = 0L } faceApi.updateFaceData(pageNo, faceEntityList) if (list.size >= pageSize) { - faceTimestamp = list.last().faceUpdateTimestamp ?: 0 + // 取当前页最大时间戳,与已累积的比较取最大值 + val pageMaxTimestamp = maxFaceTimestamp(list) + if (pageMaxTimestamp > faceTimestamp) { + faceTimestamp = pageMaxTimestamp + } currentPageNo++ getUserFaceCache( pageNo = currentPageNo, callback = callback @@ -95,7 +100,11 @@ class NetViewModelV2 : ViewModel() { return@withContext } if (list.isNotEmpty()) { - faceTimestamp = list.last().faceUpdateTimestamp ?: 0 + // 最后一页:取当前页最大时间戳,与已累积的比较取最大值 + val pageMaxTimestamp = maxFaceTimestamp(list) + if (pageMaxTimestamp > faceTimestamp) { + faceTimestamp = pageMaxTimestamp + } } SpTool.lastFaceTimestamp = faceTimestamp callback(true, "查询完成") @@ -161,7 +170,7 @@ class NetViewModelV2 : ViewModel() { /** - * 获取人脸数据 + * 增量获取人脸数据(支持分页递归,取所有页中的最大时间戳) */ fun getFaceIncrementList( pageNo: Int = 1, @@ -169,9 +178,10 @@ class NetViewModelV2 : ViewModel() { timestamp: Long, refreshFaceList: () -> Unit ) { + var currentPageNo = pageNo viewModelScope.launch { val response = repository.getFaceIncrementList( - pageNum = pageNo.toLong(), + pageNum = currentPageNo.toLong(), pageSize = pageSize.toLong(), timestamp = timestamp ) @@ -181,10 +191,27 @@ class NetViewModelV2 : ViewModel() { withContext(Dispatchers.IO) { val list = response.data ?: emptyList() updateFaceData(list) - if (list.isNotEmpty()) { - faceTimestamp = list.last().faceUpdateTimestamp ?: 0 + // 取当前页最大时间戳,与已累积的比较取最大值 + val pageMaxTimestamp = maxFaceTimestamp(list) + if (pageMaxTimestamp > faceTimestamp) { + faceTimestamp = pageMaxTimestamp + } + if (list.size >= pageSize) { + // 还有下一页,继续递归拉取 + currentPageNo++ + getFaceIncrementList( + pageNo = currentPageNo, + pageSize = pageSize, + timestamp = timestamp, + refreshFaceList = refreshFaceList + ) + return@withContext + } + // 所有页拉取完毕,保存最终时间戳(只升不降) + val storedTimestamp = SpTool.lastFaceTimestamp + if (faceTimestamp > storedTimestamp) { + SpTool.lastFaceTimestamp = faceTimestamp } - SpTool.lastFaceTimestamp = faceTimestamp try { refreshFaceList() } catch (e: Exception) { @@ -246,4 +273,11 @@ class NetViewModelV2 : ViewModel() { } } + /** + * 从人脸数据列表中取出最大的 faceUpdateTimestamp(跳过 null 值) + */ + private fun maxFaceTimestamp(list: List): Long { + return list.mapNotNull { it.faceUpdateTimestamp }.maxOrNull() ?: 0L + } + } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_plc_setting.xml b/app/src/main/res/layout/activity_plc_setting.xml index f98afc9..9a4d88e 100644 --- a/app/src/main/res/layout/activity_plc_setting.xml +++ b/app/src/main/res/layout/activity_plc_setting.xml @@ -324,6 +324,54 @@ android:textColor="#FFCC99" android:textSize="22sp" /> + + + + + + + + + + + + + + +