Author SHA1 Message Date
lvmeng 3cbf64ec14 refactor(api): 重构人脸数据添加接口参数结构
- 将 addUserFace 接口从表单参数改为请求体参数
- 新增 FeatureBody 数据类封装人脸特征和图片URL
- 更新 RemoteRepository 中的人脸数据添加方法实现
- 优化参数传递方式,提高代码可读性
- 移除 @FormUrlEncoded 注解,改用 @Body 注解
- 调整方法参数命名,统一使用 imageUrl 命名规范
2026-07-01 15:57:34 +08:00
lvmeng d1e9c4d34f refactor(config): 更新环境配置URL映射关系
- 将TEST_BASE_URL替换为LOCAL_BASE_URL作为本地开发地址
- 将UAT_BASE_URL替换为TEST_BASE_URL作为测试环境地址
- 更新环境切换对话框中的单选按钮映射逻辑
- 修改应用启动时的设备ID判断逻辑和默认环境配置
- 移除部分临时测试代码并调整注释状态
2026-06-30 16:58:11 +08:00
lvmeng 38a00e8d34 refactor(face): 更新FaceEntity实体类并调整引擎激活逻辑
- 为FaceEntity添加默认构造函数并初始化注册时间
- 为FaceEntity的所有构造函数添加@Ignore注解
- 补充FaceEntity的Parcel序列化和反序列化逻辑
- 完善FaceEntity的getter和setter方法实现
- 更新equals和hashCode方法以包含新增字段
- 将人脸引擎激活从userViewModel切换到netViewModelV2
- 添加lint配置文件忽略通知权限检查警告
2026-06-30 10:31:06 +08:00
lvmeng 95b4f5e8f2 feat(network): 添加API V2版本支持并重构网络层
- 新增ApiServiceV2接口定义,支持人脸缓存、增量数据、设备配置等功能
- 添加NetViewModelV2视图模型,实现新版本API的数据处理逻辑
- 创建RemoteRepositoryV2仓库,封装API V2的网络请求操作
- 新增UserFaceModelV2数据模型,优化人脸特征数据结构
- 添加DeviceConfigV2设备配置模型,支持更多设备参数
- 在ApiClient中注册apiServiceV2实例
- 更新BaseActivity使用NetViewModelV2替代UserViewModel
- 修改DeviceInitActivity中的人脸数据获取和设备配置逻辑
- 重构FaceDao数据库访问对象,优化用户类型查询和删除操作
- 更新FaceEntity实体类,增加更多用户相关字段
- 移除BaseViewModel中的旧版网络基础功能代码
- 优化人脸数据同步和存储流程,提升性能表现
2026-06-30 10:08:10 +08:00
21 changed files with 874 additions and 407 deletions
+5
View File
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<lint>
<!-- Glide库中的NotificationTarget触发了此检查,但本kiosk应用不使用通知功能 -->
<issue id="NotificationPermission" severity="ignore" />
</lint>
@@ -38,8 +38,8 @@ object GlobalData {
/** /**
* 具体业务 BaseUrl * 具体业务 BaseUrl
*/ */
const val TEST_BASE_URL: String = "http://192.168.1.201:14801" const val LOCAL_BASE_URL: String = "http://192.168.10.101:24801"
const val UAT_BASE_URL: String = "https://dev.yixiong-tech.com:8083" const val TEST_BASE_URL: String = "https://dev.yixiong-tech.com:8081"
const val PROD_BASE_URL: String = "https://api.dm.yixiong-tech.com:8443" const val PROD_BASE_URL: String = "https://api.dm.yixiong-tech.com:8443"
/** /**
@@ -42,12 +42,15 @@ class MyApp : App() {
} else { } else {
GlobalData.appBaseUrl = GlobalData.appBaseUrl =
if ("14d2dad5-7d33-3ced-8a59-f14cb1ef2d21" == deviceId) if ("14d2dad5-7d33-3ced-8a59-f14cb1ef2d21" == deviceId)
GlobalData.TEST_BASE_URL GlobalData.LOCAL_BASE_URL
else else
GlobalData.PROD_BASE_URL // GlobalData.PROD_BASE_URL
GlobalData.TEST_BASE_URL
} }
// TODO: 临时测试---------------- // TODO: 临时测试----------------
// GlobalData.appBaseUrl = GlobalData.TEST_BASE_URL
// GlobalData.deviceId = "79814f20-f253-397c-9da7-8430ffa5ef00"
// if ("2987f0c5-5754-33e9-b00a-251db5e2e55f" == deviceId) { // if ("2987f0c5-5754-33e9-b00a-251db5e2e55f" == deviceId) {
// GlobalData.deviceId = "79814f20-f253-397c-9da7-8430ffa5ef00" // GlobalData.deviceId = "79814f20-f253-397c-9da7-8430ffa5ef00"
// } // }
@@ -55,6 +58,7 @@ class MyApp : App() {
GlobalData.appVersion = AppUtil.getAppVersionCode(this).toString() GlobalData.appVersion = AppUtil.getAppVersionCode(this).toString()
GlobalData.globalEquipmentCode = "202501171634" GlobalData.globalEquipmentCode = "202501171634"
// GlobalData.appBaseUrl = "http://192.168.1.201:14801" // GlobalData.appBaseUrl = "http://192.168.1.201:14801"
} }
} }
@@ -25,11 +25,10 @@ import com.sw.plate.utils.arcface.viewmodel.RecognizeViewModel
import com.sw.platecabinet.R import com.sw.platecabinet.R
import com.sw.platecabinet.databinding.ItemTitleTimeBinding import com.sw.platecabinet.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.ext.clickWithCoroutines import com.sw.platecabinet.ext.clickWithCoroutines
import com.sw.platecabinet.model.response.UserFaceModel
import com.sw.platecabinet.utils.IntervalExecutor import com.sw.platecabinet.utils.IntervalExecutor
import com.sw.platecabinet.utils.SpTool import com.sw.platecabinet.utils.SpTool
import com.sw.platecabinet.view.CustomLoadingDialog import com.sw.platecabinet.view.CustomLoadingDialog
import com.sw.platecabinet.viewmodel.UserViewModel import com.sw.platecabinet.viewmodel.NetViewModelV2
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -48,10 +47,8 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
// private val permissionHelpers = mutableMapOf<Int, PermissionHelper>() // private val permissionHelpers = mutableMapOf<Int, PermissionHelper>()
// protected var keyEventHelper: ScanGunKeyEventHelper? = null // protected var keyEventHelper: ScanGunKeyEventHelper? = null
// 用户对应的viewModel
protected val userViewModel by viewModels<UserViewModel>()
public val recognizeViewModel by viewModels<RecognizeViewModel>() public val recognizeViewModel by viewModels<RecognizeViewModel>()
public val netViewModelV2 by viewModels<NetViewModelV2>()
private var startTime: Long = 0 private var startTime: Long = 0
@@ -298,84 +295,23 @@ abstract class BaseActivity<VB : ViewBinding> : AppCompatActivity() {
// private val dealyMillis = 10 * 60 * 1000L // private val dealyMillis = 10 * 60 * 1000L
private val initialDelay = 1 * 1000L private val initialDelay = 1 * 1000L
private val dealyMillis = 10 * 1000L private val dealyMillis = 10 * 1000L
fun startFaceTask(block: (List<String?>) -> Unit) { fun startFaceTask() {
faceTaskJob = faceTaskJob =
intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) { intervalExecutor.startIntervalTaskWithInitialDelay(initialDelay, dealyMillis) {
getFaceIncrementList(action = block) getFaceIncrementList()
} }
} }
private var faceTimestamp = 0L private var faceTimestamp = 0L
//private var taskPageNo = 1 //private var taskPageNo = 1
private fun getFaceIncrementList(pageNo: Int = 1, action: (List<String?>) -> Unit) { private fun getFaceIncrementList(pageNo: Int = 1) {
val timestamp = SpTool.lastFaceTimestamp val timestamp = SpTool.lastFaceTimestamp
userViewModel.getFaceIncrementList( netViewModelV2.getFaceIncrementList(
pageNo = pageNo, pageNo = pageNo,
timestamp = timestamp timestamp = timestamp
) { list -> ) {
runOnUiThread { recognizeViewModel.refreshFaceList()
if (pageNo == 1 && list.isEmpty()) {
return@runOnUiThread
}
updateFaceData(list)
val userIdList = list.map { it.userId }
action(userIdList)
if (list.isNotEmpty()) {
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
}
SpTool.lastFaceTimestamp = faceTimestamp
}
}
}
private fun updateFaceData(list: List<UserFaceModel>) {
lifecycleScope.launch(Dispatchers.IO) {
val faceList = mutableListOf<FaceEntity>()
val faceDao = FaceDatabase.getInstance(this@BaseActivity).faceDao()
try {
list.forEach { model ->
if (model.faceDeleted == true) {
//删除数据
faceDao.deleteFaceById(model.userId)
} else {
// 获取该用户所有已存特征,判断当前特征是否已存在
val existList = faceDao.queryAllByUserName(model.userId)
val featureMatches =
existList.map { Base64.encode(it.featureData) == model.faceFeatureStr }
Timber.d("userId=${model.userId}, 已存记录数=${existList.size}, 各条特征是否与服务器一致=$featureMatches")
val alreadyExists = featureMatches.any { it }
if (!alreadyExists) {
//当前特征不存在,保存
runCatching { getNewFaceEntity(model) }
.onSuccess { faceList.add(it) }
.onFailure { Timber.e(it, "人脸特征解码失败: userId=${model.userId}") }
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
try {
if (faceList.isNotEmpty()) {
FaceDatabase.getInstance(this@BaseActivity).faceDao().insert(faceList)
}
recognizeViewModel.refreshFaceList()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
private fun getNewFaceEntity(model: UserFaceModel): FaceEntity {
return FaceEntity(
model.userId,
null,
Base64.decode(model.faceFeatureStr)
).also {
//1-会员、2-临时用户
it.userType = if (model.member) "1" else "2"
it.cardNo = model.cardNo
} }
} }
@@ -3,7 +3,6 @@ package com.sw.platecabinet.activity
import android.content.Intent import android.content.Intent
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.FaceApi
import com.sw.platecabinet.GlobalData import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.databinding.ActivityDeviceInitBinding import com.sw.platecabinet.databinding.ActivityDeviceInitBinding
import com.sw.platecabinet.databinding.ItemTitleTimeBinding import com.sw.platecabinet.databinding.ItemTitleTimeBinding
@@ -32,7 +31,7 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
//val count = FaceApi().queryFaceCount() //val count = FaceApi().queryFaceCount()
//if (count == 0) { //if (count == 0) {
if (SpTool.firstGetFace) { if (SpTool.firstGetFace) {
userViewModel.getUserFaceCache { status, msg -> netViewModelV2.getUserFaceCache { status, msg ->
SpTool.firstGetFace = false SpTool.firstGetFace = false
getDeviceConfig() getDeviceConfig()
} }
@@ -44,7 +43,7 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
} }
private fun getDeviceConfig() { private fun getDeviceConfig() {
userViewModel.getDeviceConfig { deviceConfig -> netViewModelV2.getDeviceConfig(onSuccess = { deviceConfig ->
runOnUiThread { runOnUiThread {
if (deviceConfig == null) { if (deviceConfig == null) {
hideWaitingDialog() hideWaitingDialog()
@@ -61,7 +60,11 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
goLoginActivity() goLoginActivity()
}, 500) }, 500)
} }
} }, onFailure = { errMsg ->
runOnUiThread {
ToastUtils.showToast(errMsg ?: "接口异常")
}
})
} }
override fun registerDataChange() { override fun registerDataChange() {
@@ -20,7 +20,6 @@ import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import com.arcsoft.face.ErrorInfo import com.arcsoft.face.ErrorInfo
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.gson.Gson
import com.sw.inbound.utils.DateTimeUtils import com.sw.inbound.utils.DateTimeUtils
import com.sw.plate.utils.Base64 import com.sw.plate.utils.Base64
import com.sw.plate.utils.ToastUtils import com.sw.plate.utils.ToastUtils
@@ -33,7 +32,6 @@ import com.sw.plate.utils.arcface.PreviewConfig
import com.sw.plate.utils.arcface.camera.CameraListener import com.sw.plate.utils.arcface.camera.CameraListener
import com.sw.plate.utils.arcface.camera.DualCameraHelper import com.sw.plate.utils.arcface.camera.DualCameraHelper
import com.sw.plate.utils.arcface.face.constants.LivenessType import com.sw.plate.utils.arcface.face.constants.LivenessType
import com.sw.plate.utils.arcface.face.constants.RecognizeColor
import com.sw.plate.utils.arcface.face.model.CompareResult import com.sw.plate.utils.arcface.face.model.CompareResult
import com.sw.plate.utils.arcface.face.model.FacePreviewInfo import com.sw.plate.utils.arcface.face.model.FacePreviewInfo
import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration import com.sw.plate.utils.arcface.face.model.RecognizeConfiguration
@@ -49,7 +47,7 @@ import com.sw.platecabinet.dialog.CustomDialog
import com.sw.platecabinet.ext.clickWithDebounce import com.sw.platecabinet.ext.clickWithDebounce
import com.sw.platecabinet.ext.gone import com.sw.platecabinet.ext.gone
import com.sw.platecabinet.ext.visible import com.sw.platecabinet.ext.visible
import com.sw.platecabinet.model.response.UserFaceModel import com.sw.platecabinet.model.response.UserFaceModelV2
import com.sw.platecabinet.utils.BitmapSaver import com.sw.platecabinet.utils.BitmapSaver
import com.sw.platecabinet.utils.CountDownUtil import com.sw.platecabinet.utils.CountDownUtil
import com.sw.platecabinet.utils.Debouncer import com.sw.platecabinet.utils.Debouncer
@@ -92,7 +90,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
} }
override fun initialize() { override fun initialize() {
userViewModel.activeEngine() netViewModelV2.activeEngine()
PlcHelper.getInstance(this@LoginByFaceActivity) PlcHelper.getInstance(this@LoginByFaceActivity)
.openSerialPort(object : PlcCallback<String> { .openSerialPort(object : PlcCallback<String> {
@@ -113,9 +111,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
EventBus.getDefault().register(this) EventBus.getDefault().register(this)
//开启人脸增量数据定时任务 //开启人脸增量数据定时任务
startFaceTask { startFaceTask()
loadLogInfo("collectFace增量接口数据用户id:${Gson().toJson(it)}")
}
val soundMap = hashMapOf<String, Int>() val soundMap = hashMapOf<String, Int>()
soundMap[COLLECT_SUCCESS] = R.raw.collect_success soundMap[COLLECT_SUCCESS] = R.raw.collect_success
@@ -887,7 +883,7 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
} }
private fun addUserFace(faceData: String, imageFile: File) { private fun addUserFace(faceData: String, imageFile: File) {
userViewModel.addUserFace(faceData, imageFile) { status, faceModel -> netViewModelV2.addUserFace(faceData, imageFile) { status, faceModel ->
loadLogInfo("collectFace,saveFaceInfo: ----------12,人脸数据发送完成时间:${DateTimeUtils.getDateTimeString()},结果:$status-----------") loadLogInfo("collectFace,saveFaceInfo: ----------12,人脸数据发送完成时间:${DateTimeUtils.getDateTimeString()},结果:$status-----------")
if (status.not()) { if (status.not()) {
ToastUtils.showToast("新增数据失败") ToastUtils.showToast("新增数据失败")
@@ -943,18 +939,11 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
private var currentUserId: String? = null private var currentUserId: String? = null
private fun saveFaceInfo(faceModel: UserFaceModel, block: () -> Unit = {}) { private fun saveFaceInfo(faceModel: UserFaceModelV2, block: () -> Unit = {}) {
try { try {
loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------") loadLogInfo("collectFace,saveFaceInfo: ----------8,保存人脸取特征数据-----------")
val faceDao = FaceDatabase.getInstance(this).faceDao() val faceDao = FaceDatabase.getInstance(this).faceDao()
val faceEntity = FaceEntity( val faceEntity = faceModel.toFaceEntity()
faceModel.userId,
null,
Base64.decode(faceModel.faceFeatureStr)
).also {
//1-会员、2-临时用户
it.userType = if (faceModel.member) "1" else "2"
}
val faceId = faceDao.insert(faceEntity) val faceId = faceDao.insert(faceEntity)
faceEntity.faceId = faceId faceEntity.faceId = faceId
loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------") loadLogInfo("collectFace,saveFaceInfo: ----------10,人脸数据保存完成-----------")
@@ -68,7 +68,7 @@ class PlcSettingActivity : BaseActivity<ActivityPlcSettingBinding>() {
FaceServer.getInstance().clearAllFaces() FaceServer.getInstance().clearAllFaces()
} }
} }
userViewModel.getUserFaceCache(pageNo = 1, callback = { state, errMsg -> netViewModelV2.getUserFaceCache(pageNo = 1, callback = { state, errMsg ->
hideWaitingDialog() hideWaitingDialog()
if (state) { if (state) {
recognizeViewModel.refreshFaceList() recognizeViewModel.refreshFaceList()
@@ -39,8 +39,8 @@ class EnvSwitchDialog(
override fun initView() { override fun initView() {
// 根据当前 appBaseUrl 预选对应 RadioButton,不匹配则不选 // 根据当前 appBaseUrl 预选对应 RadioButton,不匹配则不选
when (GlobalData.appBaseUrl) { when (GlobalData.appBaseUrl) {
GlobalData.TEST_BASE_URL -> binding.rbTest.isChecked = true GlobalData.LOCAL_BASE_URL -> binding.rbTest.isChecked = true
GlobalData.UAT_BASE_URL -> binding.rbUat.isChecked = true GlobalData.TEST_BASE_URL -> binding.rbUat.isChecked = true
GlobalData.PROD_BASE_URL -> binding.rbProd.isChecked = true GlobalData.PROD_BASE_URL -> binding.rbProd.isChecked = true
} }
@@ -55,8 +55,8 @@ class EnvSwitchDialog(
return@setOnClickListener return@setOnClickListener
} }
val newUrl = when (binding.rgEnv.checkedRadioButtonId) { val newUrl = when (binding.rgEnv.checkedRadioButtonId) {
binding.rbTest.id -> GlobalData.TEST_BASE_URL binding.rbTest.id -> GlobalData.LOCAL_BASE_URL
binding.rbUat.id -> GlobalData.UAT_BASE_URL binding.rbUat.id -> GlobalData.TEST_BASE_URL
binding.rbProd.id -> GlobalData.PROD_BASE_URL binding.rbProd.id -> GlobalData.PROD_BASE_URL
else -> return@setOnClickListener else -> return@setOnClickListener
} }
@@ -1,5 +1,7 @@
package com.sw.platecabinet.model package com.sw.platecabinet.model
import com.google.gson.annotations.SerializedName
/** /**
* 错误信息 code == 200 成功 * 错误信息 code == 200 成功
*/ */
@@ -19,6 +21,27 @@ data class DeviceConfig(
var arcsoftActiveKey: String? = null var arcsoftActiveKey: String? = null
) )
data class DeviceConfigV2(
/** 业务服务器 BASE URL */
val appPackageUrl: String? = null,
/** 食堂/餐厅名称 */
val canteenName: String? = null,
/** 食堂/餐厅 ID(后续所有业务 API 的 restId */
val canteenId: String? = null,
/** 虹软人脸 SDK App ID */
val arcsoftAppId: String? = null,
/** 虹软人脸 SDK Key */
val arcsoftSdkKey: String? = null,
/** 虹软人脸 SDK Active Key */
val arcsoftActiveKey: String? = null,
/** MQTT 客户端服务器 IP */
val clientServerIp: String? = null,
/** 智慧食堂服务器 IP(MQTT 端口) */
val zhstServerIp: String? = null
)
data class CodeMsg( data class CodeMsg(
val code: String? = "", val code: String? = "",
val msg: String? = "" val msg: String? = ""
@@ -0,0 +1,6 @@
package com.sw.platecabinet.model.request
data class FeatureBody(
val url:String,
val featureChar:String
)
@@ -9,19 +9,6 @@ import kotlinx.parcelize.Parcelize
*/ */
@Parcelize @Parcelize
data class UserFaceModel( data class UserFaceModel(
// @SerializedName("faceFeature")
// val faceFeature: String? = "",
// @SerializedName("faceFeatureString")
// val faceFeatureString: String? = "",
// @SerializedName("faceType")
// val faceType: String? = "",
// @SerializedName("userFaceId")
// val userFaceId: String? = "",
// @SerializedName("userId")
// val userId: String? = "",
// @SerializedName("updateTimeStamp")
// val updateTimeStamp: Long = 0,
val userId: String? = "", val userId: String? = "",
val faceFeatureStr: String? = "", val faceFeatureStr: String? = "",
val faceUpdateTimestamp: Long? = null, val faceUpdateTimestamp: Long? = null,
@@ -32,10 +19,10 @@ data class UserFaceModel(
/** /**
* 会员编号 * 会员编号
*/ */
val cardNo: String, val cardNo: String? = null,
/** /**
* 是否会员 * 是否会员
*/ */
val member: Boolean val member: Boolean? = null
) : Parcelable ) : Parcelable
@@ -0,0 +1,44 @@
package com.sw.platecabinet.model.response
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
import kotlinx.parcelize.Parcelize
/**
* 用户人脸信息
*/
@Parcelize
data class UserFaceModelV2(
val userFaceId: String? = null,
val userId: String? = null,
val faceFeature: String? = null,
val faceFeatureStr: String? = null,
val faceFeatureString: String? = null,
val faceUpdateTimestamp: Long? = null,
val cardNo: String? = null,
val member: Boolean? = false,
val faceDeleted: Boolean? = false,
val personType: String? = null
) : Parcelable {
fun resolveFeatureStr(): String? {
return faceFeature?.takeIf { it.isNotEmpty() }
?: faceFeatureStr?.takeIf { it.isNotEmpty() }
?: faceFeatureString?.takeIf { it.isNotEmpty() }
}
fun toFaceEntity(): FaceEntity {
val featureBase64 = resolveFeatureStr()
return FaceEntity(
userId,
android.util.Base64.decode(featureBase64, android.util.Base64.DEFAULT),
personType,
cardNo,
userId,
userFaceId,
member ?: false,
faceUpdateTimestamp ?: 0L
)
}
}
@@ -3,6 +3,7 @@ package com.sw.platecabinet.network
import com.sw.platecabinet.GlobalData import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.MyApp import com.sw.platecabinet.MyApp
import com.sw.platecabinet.network.api.ApiService import com.sw.platecabinet.network.api.ApiService
import com.sw.platecabinet.network.api.ApiServiceV2
import com.sw.platecabinet.network.interceptor.RequestInterceptor import com.sw.platecabinet.network.interceptor.RequestInterceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor import okhttp3.logging.HttpLoggingInterceptor
@@ -37,4 +38,8 @@ object ApiClient {
val apiService: ApiService by lazy { val apiService: ApiService by lazy {
retrofit.create(ApiService::class.java) retrofit.create(ApiService::class.java)
} }
val apiServiceV2: ApiServiceV2 by lazy {
retrofit.create(ApiServiceV2::class.java)
}
} }
@@ -0,0 +1,65 @@
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
import okhttp3.MultipartBody
import retrofit2.http.Body
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Url
interface ApiServiceV2 {
/**
* 获取人脸缓存数据
*/
@POST
suspend fun getUserFaceCache(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/common/face/page",
@Body param: Map<String, Int>
): ApiResponse<List<UserFaceModelV2>>
/**
* 获取人脸增量数据
*/
@POST
suspend fun getFaceIncrementList(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/common/face/increment",
@Body param: Map<String, Long>
): ApiResponse<List<UserFaceModelV2>?>
/**
* 获取设备配置数据
*/
@GET
suspend fun getDeviceConfig(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/pickup/device/config"
): ApiResponse<DeviceConfigV2?>
/**
* 添加人脸数据
*/
@POST
suspend fun addUserFace(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/user/add-by-face",
@Body faceData: FeatureBody
): ApiResponse<UserFaceModelV2?>
/**
* 上传图片
*/
@Multipart
@POST
suspend fun uploadImage(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/upload",
@Part file: MultipartBody.Part,
): ApiResponse<String?>
}
@@ -0,0 +1,72 @@
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
import com.sw.platecabinet.network.api.ApiServiceV2
import com.sw.platecabinet.utils.FileUtils
import java.io.File
/**
* 远程数据处理
*/
class RemoteRepositoryV2 constructor(
private val apiService: ApiServiceV2
) : BaseRepository() {
/**
* 获取人脸数据
*/
suspend fun getUserFaceCache(
pageNum: Int,
pageSize: Int = 100,
): ApiResponse<List<UserFaceModelV2>> {
return safeApiCall {
apiService.getUserFaceCache(
param = mapOf(
"pageNum" to pageNum, "pageSize" to pageSize
)
)
}
}
suspend fun getFaceIncrementList(
pageNum: Long, pageSize: Long = 100L, timestamp: Long
): ApiResponse<List<UserFaceModelV2>?> {
return safeApiCall {
apiService.getFaceIncrementList(
param = mapOf(
"pageNum" to pageNum, "pageSize" to pageSize, "timestamp" to timestamp
)
)
}
}
suspend fun getDeviceConfig(): ApiResponse<DeviceConfigV2?> {
return safeApiCall { apiService.getDeviceConfig() }
}
suspend fun addUserFace(faceData: String, imageUrl: String): ApiResponse<UserFaceModelV2?> {
return safeApiCall {
apiService.addUserFace(
faceData = FeatureBody(
url = imageUrl, featureChar = faceData
)
)
}
}
suspend fun uploadImage(file: File): ApiResponse<String?> {
return safeApiCall {
val part = FileUtils.genRequestPart(file)
if (part == null) {
ApiResponse(code = "-1", msg = "解析图片失败")
} else {
apiService.uploadImage(file = part)
}
}
}
}
@@ -1,74 +1,74 @@
package com.sw.platecabinet.viewmodel //package com.sw.platecabinet.viewmodel
//
import androidx.lifecycle.ViewModel //import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope //import androidx.lifecycle.viewModelScope
import com.sw.plate.utils.ToastUtils //import com.sw.plate.utils.ToastUtils
import com.sw.platecabinet.model.response.ApiResponse //import com.sw.platecabinet.model.response.ApiResponse
import com.sw.platecabinet.network.ApiClient //import com.sw.platecabinet.network.ApiClient
import com.sw.platecabinet.repository.RemoteRepository //import com.sw.platecabinet.repository.RemoteRepository
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 timber.log.Timber //import timber.log.Timber
//
abstract class BaseViewModel() : ViewModel() { //abstract class BaseViewModel() : ViewModel() {
protected val repository = RemoteRepository(ApiClient.apiService) // protected val repository = RemoteRepository(ApiClient.apiService)
//
// 显示进度条 // // 显示进度条
private val _showLoading = MutableStateFlow<Boolean>(false) // private val _showLoading = MutableStateFlow<Boolean>(false)
val showLoading: StateFlow<Boolean> = _showLoading // val showLoading: StateFlow<Boolean> = _showLoading
//
/** // /**
* 带进度条的请求 // * 带进度条的请求
*/ // */
protected fun launchWithLoading(block: suspend () -> Unit) { // protected fun launchWithLoading(block: suspend () -> Unit) {
viewModelScope.launch { // viewModelScope.launch {
try { // try {
_showLoading.value = true // _showLoading.value = true
block() // block()
} catch (e: Exception) { // } catch (e: Exception) {
// 错误处理可被子类重写 // // 错误处理可被子类重写
handleError(e) // handleError(e)
} finally { // } finally {
_showLoading.value = false // _showLoading.value = false
} // }
} // }
}
/**
* 不带进度条的请求
*/
protected fun launch(block: suspend () -> Unit) {
viewModelScope.launch() {
try {
block()
} catch (e: Exception) {
// 错误处理可被子类重写
handleError(e)
}
}
}
protected open fun parseResponse(response: ApiResponse<*>): Boolean {
if (response.isSuccess()) {
return true
}
Timber.d("msg = ${response.msg}, code = ${response.code}")
ToastUtils.showToast("${response.msg}(${response.code})")
return false
}
fun handleError(exception: Exception) {
Timber.d("handleError ${exception.message}")
}
// fun parseEquipmentInfo(equipmentInfo: EquipmentInfo) {
//// GlobalData.appBaseUrl = equipmentInfo.appPackageUrl!!
//// GlobalData.appBaseUrl="https://vip.shuziweidao.com"
//// GlobalData.sdkKey = equipmentInfo.arcsoftSdkKey!!
//// GlobalData.appId = equipmentInfo.arcsoftAppId!!
//// GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!!
//// GlobalData.restId = equipmentInfo.canteenId!!
// } // }
} //
// /**
// * 不带进度条的请求
// */
// protected fun launch(block: suspend () -> Unit) {
// viewModelScope.launch() {
// try {
// block()
// } catch (e: Exception) {
// // 错误处理可被子类重写
// handleError(e)
// }
// }
// }
//
// protected open fun parseResponse(response: ApiResponse<*>): Boolean {
// if (response.isSuccess()) {
// return true
// }
// Timber.d("msg = ${response.msg}, code = ${response.code}")
// ToastUtils.showToast("${response.msg}(${response.code})")
// return false
// }
//
//
// fun handleError(exception: Exception) {
// Timber.d("handleError ${exception.message}")
// }
//
//// fun parseEquipmentInfo(equipmentInfo: EquipmentInfo) {
////// GlobalData.appBaseUrl = equipmentInfo.appPackageUrl!!
////// GlobalData.appBaseUrl="https://vip.shuziweidao.com"
////// GlobalData.sdkKey = equipmentInfo.arcsoftSdkKey!!
////// GlobalData.appId = equipmentInfo.arcsoftAppId!!
////// GlobalData.activeKey = equipmentInfo.arcsoftActiveKey!!
////// GlobalData.restId = equipmentInfo.canteenId!!
//// }
//}
@@ -0,0 +1,249 @@
package com.sw.platecabinet.viewmodel
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.arcsoft.face.ErrorInfo
import com.sw.inbound.utils.SPUtil
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.FaceDatabase
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.model.DeviceConfigV2
import com.sw.platecabinet.model.response.UserFaceModel
import com.sw.platecabinet.model.response.UserFaceModelV2
import com.sw.platecabinet.network.ApiClient
import com.sw.platecabinet.repository.RemoteRepositoryV2
import com.sw.platecabinet.utils.SpTool
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.io.File
/**
* 用户viewmodel
*/
class NetViewModelV2 : ViewModel() {
companion object {
const val PAGE_SIZE = 100
}
private val faceApi: FaceApi = FaceApi()
private val repository = RemoteRepositoryV2(ApiClient.apiServiceV2)
//fun getUserFace(
// pageNo: Int,
// pageSize: Int,
// callback: (Boolean, List<UserFaceModelV2>) -> Unit
//) {
// viewModelScope.launch {
// val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize)
// if (response.isSuccess()) callback(true, response.data ?: emptyList())
// else callback(false, emptyList())
// }
//}
/**
* 获取人脸数据
*/
fun getUserFaceCache(
pageNo: Int = 1,
pageSize: Int = PAGE_SIZE,
callback: (Boolean, String?) -> Unit = { _, _ -> }
) {
var currentPageNo = pageNo
viewModelScope.launch {
val response = repository.getUserFaceCache(pageNo, pageSize)
if (response.isSuccess().not()) {
callback(false, response.msg)
return@launch
}
// 获取成功一次后缓存状态
SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true)
val list = response.data ?: emptyList()
if (pageNo == 1 && list.isEmpty()) {
callback(false, "暂无数据")
return@launch
}
val faceEntityList = list.mapNotNull { face ->
val featureBase64 = face.resolveFeatureStr()
if (featureBase64.isNullOrBlank()) null
else {
val featureData = base64ToByteArray(featureBase64)
if (featureData == null) null
else face.toFaceEntity()
}
}
withContext(Dispatchers.Default) {
if (currentPageNo == 1) {
//第一页数据删除本地数据库
faceApi.clearFaceData()
}
faceApi.updateFaceData(pageNo, faceEntityList)
if (list.size >= pageSize) {
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
currentPageNo++
getUserFaceCache(
pageNo = currentPageNo, callback = callback
)
return@withContext
}
if (list.isNotEmpty()) {
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
}
SpTool.lastFaceTimestamp = faceTimestamp
callback(true, "查询完成")
}
}
}
private var faceTimestamp = 0L
/**
* 激活人脸识别引擎
*/
fun activeEngine() {
Timber.d("activeEngine")
faceApi.activeEngine(
App.getContext(),
GlobalData.appId,
GlobalData.sdkKey,
GlobalData.activeKey,
object : FaceApi.ActiveCallback {
override fun onSuccess(activeCode: Int) {
Timber.d("activeEngine activeCode = $activeCode")
// ThreadUtils.launch {
viewModelScope.launch(Dispatchers.Main) {
when (activeCode) {
ErrorInfo.MOK -> {
ToastUtils.showToast("激活引擎成功")
}
ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> {
// ToastUtils.showToast("引擎已激活,无需再次激活")
}
else -> {
ToastUtils.showToast("激活引擎失败($activeCode)")
}
}
}
}
override fun onFail(e: Exception?) {
viewModelScope.launch(Dispatchers.Main) {
ToastUtils.showToast("激活引擎异常,${e?.message}")
}
}
})
}
fun getDeviceConfig(onSuccess: (DeviceConfigV2?) -> Unit, onFailure: (String?) -> Unit) {
// Timber.tag(TAG).d("getDeviceConfig")
viewModelScope.launch {
val response = repository.getDeviceConfig()
if (response.isSuccess()) {
onSuccess(response.data)
} else {
onFailure(response.msg)
}
}
}
/**
* 获取人脸数据
*/
fun getFaceIncrementList(
pageNo: Int = 1,
pageSize: Int = PAGE_SIZE,
timestamp: Long,
refreshFaceList: () -> Unit
) {
viewModelScope.launch {
val response = repository.getFaceIncrementList(
pageNum = pageNo.toLong(),
pageSize = pageSize.toLong(),
timestamp = timestamp
)
if (response.isSuccess().not()) {
return@launch
}
withContext(Dispatchers.IO) {
val list = response.data ?: emptyList()
updateFaceData(list)
if (list.isNotEmpty()) {
faceTimestamp = list.last().faceUpdateTimestamp ?: 0
}
SpTool.lastFaceTimestamp = faceTimestamp
try {
refreshFaceList()
} catch (e: Exception) {
e.printStackTrace()
}
}
}
}
private fun updateFaceData(list: List<UserFaceModelV2>) {
try {
list.forEach { model ->
if (model.faceDeleted == true) {
//删除数据
faceApi.deleteByUserName(model.userId)
} else {
// 获取该用户所有已存特征,判断当前特征是否已存在
val existList = faceApi.queryAllByUserName(model.userId)
val featureMatches =
existList.map { Base64.encode(it.featureData) == model.faceFeatureStr }
Timber.d("userId=${model.userId}, 已存记录数=${existList.size}, 各条特征是否与服务器一致=$featureMatches")
val alreadyExists = featureMatches.any { it }
if (!alreadyExists) {
faceApi.insert(model.toFaceEntity())
}
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* 添加人脸数据
*/
fun addUserFace(faceData: String, imageFile: File, block: (Boolean, UserFaceModelV2?) -> Unit) {
viewModelScope.launch {
val imageResp = repository.uploadImage(imageFile)
val imageUrl = imageResp.data ?: ""
if (imageUrl.isBlank()) {
block(false, null)
return@launch
}
val faceResp = repository.addUserFace(faceData, imageUrl)
if (faceResp.isSuccess().not()) {
block(false, null)
return@launch
}
block(true, faceResp.data)
}
}
private fun base64ToByteArray(base64: String?): ByteArray? {
base64 ?: return null
return try {
Base64.decode(base64)
} catch (e: Exception) {
null
}
}
}
@@ -1,201 +1,201 @@
package com.sw.platecabinet.viewmodel //package com.sw.platecabinet.viewmodel
//
import androidx.lifecycle.viewModelScope //import androidx.lifecycle.viewModelScope
import com.arcsoft.face.ErrorInfo //import com.arcsoft.face.ErrorInfo
import com.sw.inbound.utils.SPUtil //import com.sw.inbound.utils.SPUtil
import com.sw.plate.App //import com.sw.plate.App
import com.sw.plate.utils.Base64 //import com.sw.plate.utils.Base64
import com.sw.plate.utils.ToastUtils //import com.sw.plate.utils.ToastUtils
import com.sw.plate.utils.arcface.FaceApi //import com.sw.plate.utils.arcface.FaceApi
import com.sw.plate.utils.arcface.facedb.entity.FaceEntity //import com.sw.plate.utils.arcface.facedb.entity.FaceEntity
import com.sw.platecabinet.GlobalData //import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.GlobalKey //import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.model.DeviceConfig //import com.sw.platecabinet.model.DeviceConfig
import com.sw.platecabinet.model.response.UserFaceModel //import com.sw.platecabinet.model.response.UserFaceModel
import com.sw.platecabinet.utils.SpTool //import com.sw.platecabinet.utils.SpTool
import kotlinx.coroutines.Dispatchers //import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch //import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext //import kotlinx.coroutines.withContext
import timber.log.Timber //import timber.log.Timber
import java.io.File //import java.io.File
//
/** ///**
* 用户viewmodel // * 用户viewmodel
*/ // */
class UserViewModel : BaseViewModel() { //class UserViewModel : BaseViewModel() {
//
companion object { // companion object {
const val PAGE_SIZE = 100 // const val PAGE_SIZE = 100
} // }
//
private val faceApi: FaceApi = FaceApi() // private val faceApi: FaceApi = FaceApi()
//
//
fun getUserFace( // fun getUserFace(
pageNo: Int, // pageNo: Int,
pageSize: Int, // pageSize: Int,
callback: (List<UserFaceModel>) -> Unit // callback: (List<UserFaceModel>) -> Unit
) { // ) {
launchWithLoading { // launchWithLoading {
val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize) // val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize)
val status = parseResponse(response) // val status = parseResponse(response)
if (status) callback(response.data ?: emptyList()) // if (status) callback(response.data ?: emptyList())
else emptyList<UserFaceModel>() // else emptyList<UserFaceModel>()
} // }
} // }
//
/** // /**
* 获取人脸数据 // * 获取人脸数据
*/ // */
fun getUserFaceCache( // fun getUserFaceCache(
pageNo: Int = 1, // pageNo: Int = 1,
pageSize: Int = PAGE_SIZE, // pageSize: Int = PAGE_SIZE,
callback: (Boolean, String?) -> Unit = { statue, msg -> } // callback: (Boolean, String?) -> Unit = { statue, msg -> }
) { // ) {
var currentPageNo = pageNo // var currentPageNo = pageNo
launchWithLoading { // launchWithLoading {
val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize) // val response = repository.getUserFaceCache(pageNum = pageNo, pageSize = pageSize)
val status = parseResponse(response) // val status = parseResponse(response)
if (status.not()) { // if (status.not()) {
callback(false, response.msg) // callback(false, response.msg)
return@launchWithLoading // return@launchWithLoading
} // }
// 获取成功一次后缓存状态 // // 获取成功一次后缓存状态
SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true) // SPUtil.getInstance().put(GlobalKey.KEY_FIRST_RUN, true)
val list: List<UserFaceModel> = response.data ?: emptyList() // val list: List<UserFaceModel> = response.data ?: emptyList()
if (pageNo == 1 && list.isEmpty()) { // if (pageNo == 1 && list.isEmpty()) {
callback(false, "暂无数据") // callback(false, "暂无数据")
return@launchWithLoading // return@launchWithLoading
} // }
val faceEntityList = list.map { // val faceEntityList = list.map {
FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr)).apply { // FaceEntity(it.userId, null, Base64.decode(it.faceFeatureStr)).apply {
userType = if (it.member) "1" else "2" // userType = if (it.member == true) "1" else "2"
} // }
} // }
withContext(Dispatchers.Default) { // withContext(Dispatchers.Default) {
if (currentPageNo == 1) { // if (currentPageNo == 1) {
//第一页数据删除本地数据库 // //第一页数据删除本地数据库
faceApi.clearFaceData() // faceApi.clearFaceData()
} // }
faceApi.updateFaceData(pageNo, faceEntityList) // faceApi.updateFaceData(pageNo, faceEntityList)
if (list.size >= pageSize) { // if (list.size >= pageSize) {
faceTimestamp = list.last().faceUpdateTimestamp ?: 0 // faceTimestamp = list.last().faceUpdateTimestamp ?: 0
currentPageNo++ // currentPageNo++
getUserFaceCache( // getUserFaceCache(
pageNo = currentPageNo, // pageNo = currentPageNo,
callback = callback // callback = callback
) // )
return@withContext // return@withContext
} // }
if (list.isNotEmpty()) { // if (list.isNotEmpty()) {
faceTimestamp = list.last().faceUpdateTimestamp ?: 0 // faceTimestamp = list.last().faceUpdateTimestamp ?: 0
} // }
SpTool.lastFaceTimestamp = faceTimestamp // SpTool.lastFaceTimestamp = faceTimestamp
callback(true, "查询完成") // callback(true, "查询完成")
} // }
} // }
} // }
//
private var faceTimestamp = 0L // private var faceTimestamp = 0L
//
//
/** // /**
* 激活人脸识别引擎 // * 激活人脸识别引擎
*/ // */
fun activeEngine() { // fun activeEngine() {
Timber.d("activeEngine") // Timber.d("activeEngine")
//
faceApi.activeEngine( // faceApi.activeEngine(
App.getContext(), // App.getContext(),
GlobalData.appId, // GlobalData.appId,
GlobalData.sdkKey, // GlobalData.sdkKey,
GlobalData.activeKey, // GlobalData.activeKey,
object : FaceApi.ActiveCallback { // object : FaceApi.ActiveCallback {
override fun onSuccess(activeCode: Int) { // override fun onSuccess(activeCode: Int) {
Timber.d("activeEngine activeCode = $activeCode") // Timber.d("activeEngine activeCode = $activeCode")
// ThreadUtils.launch { //// ThreadUtils.launch {
viewModelScope.launch(Dispatchers.Main) { // viewModelScope.launch(Dispatchers.Main) {
when (activeCode) { // when (activeCode) {
ErrorInfo.MOK -> { // ErrorInfo.MOK -> {
ToastUtils.showToast("激活引擎成功") // ToastUtils.showToast("激活引擎成功")
} // }
//
ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> { // ErrorInfo.MERR_ASF_ALREADY_ACTIVATED -> {
// ToastUtils.showToast("引擎已激活,无需再次激活") //// ToastUtils.showToast("引擎已激活,无需再次激活")
} // }
//
else -> { // else -> {
ToastUtils.showToast("激活引擎失败($activeCode)") // ToastUtils.showToast("激活引擎失败($activeCode)")
} // }
} // }
} // }
} // }
//
override fun onFail(e: Exception?) { // override fun onFail(e: Exception?) {
viewModelScope.launch(Dispatchers.Main) { // viewModelScope.launch(Dispatchers.Main) {
ToastUtils.showToast("激活引擎异常,${e?.message}") // ToastUtils.showToast("激活引擎异常,${e?.message}")
} // }
} // }
}) // })
} // }
//
//
fun getDeviceConfig(block: (DeviceConfig?) -> Unit) { // fun getDeviceConfig(block: (DeviceConfig?) -> Unit) {
// Timber.tag(TAG).d("getDeviceConfig") //// Timber.tag(TAG).d("getDeviceConfig")
launch { // launch {
val response = repository.getDeviceConfig() // val response = repository.getDeviceConfig()
if (parseResponse(response)) { // if (parseResponse(response)) {
block(response.data) // block(response.data)
} else { // } else {
block(null) // block(null)
} // }
} // }
} // }
//
//
/** // /**
* 获取人脸数据 // * 获取人脸数据
*/ // */
fun getFaceIncrementList( // fun getFaceIncrementList(
pageNo: Int = 1, // pageNo: Int = 1,
pageSize: Int = PAGE_SIZE, // pageSize: Int = PAGE_SIZE,
timestamp: Long, // timestamp: Long,
block: (List<UserFaceModel>) -> Unit // block: (List<UserFaceModel>) -> Unit
) { // ) {
launch { // launch {
val response = repository.getFaceIncrementList( // val response = repository.getFaceIncrementList(
pageNum = pageNo.toLong(), // pageNum = pageNo.toLong(),
pageSize = pageSize.toLong(), // pageSize = pageSize.toLong(),
timestamp = timestamp // timestamp = timestamp
) // )
if (parseResponse(response)) { // if (parseResponse(response)) {
withContext(Dispatchers.Default) { // withContext(Dispatchers.Default) {
val list: List<UserFaceModel> = response.data ?: emptyList() // val list: List<UserFaceModel> = response.data ?: emptyList()
block(list) // block(list)
} // }
} // }
} // }
} // }
//
/** // /**
* 添加人脸数据 // * 添加人脸数据
*/ // */
fun addUserFace(faceData: String, imageFile: File, block: (Boolean, UserFaceModel?) -> Unit) { // fun addUserFace(faceData: String, imageFile: File, block: (Boolean, UserFaceModel?) -> Unit) {
launch { // launch {
val imageResp = repository.uploadImage(imageFile) // val imageResp = repository.uploadImage(imageFile)
val imageUrl = imageResp.data ?: "" // val imageUrl = imageResp.data ?: ""
if (imageUrl.isBlank()) { // if (imageUrl.isBlank()) {
block(false, null) // block(false, null)
return@launch // return@launch
} // }
val faceResp = repository.addUserFace(faceData, imageUrl) // val faceResp = repository.addUserFace(faceData, imageUrl)
val status = parseResponse(faceResp) // val status = parseResponse(faceResp)
if (status.not()) { // if (status.not()) {
block(false, null) // block(false, null)
return@launch // return@launch
} // }
block(true, faceResp.data) // block(true, faceResp.data)
} // }
} // }
//
} //}
@@ -32,7 +32,7 @@ public class FaceApi {
*/ */
public void updateFaceData(int index, List<FaceEntity> list) { public void updateFaceData(int index, List<FaceEntity> list) {
Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size()); Log.d(TAG, "updateFaceData: index = " + index + ", listSize = " + list.size());
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); FaceDao faceDao = getFaceDao();
//if (index == 1) { //if (index == 1) {
// faceDao.deleteAll(); // faceDao.deleteAll();
// faceDao.resetId(); // faceDao.resetId();
@@ -41,7 +41,7 @@ public class FaceApi {
} }
public void clearFaceData() { public void clearFaceData() {
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); FaceDao faceDao = getFaceDao();
faceDao.deleteAll(); faceDao.deleteAll();
faceDao.resetId(); faceDao.resetId();
} }
@@ -88,29 +88,33 @@ public class FaceApi {
} }
public void deleteByUserName(String userName) { public void deleteByUserName(String userName) {
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); getFaceDao().deleteFaceById(userName);
faceDao.deleteFaceById(userName);
} }
public Long insert(FaceEntity entity) { public Long insert(FaceEntity entity) {
if (entity == null) { if (entity == null) {
return 0L; return 0L;
} }
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); return getFaceDao().insert(entity);
return faceDao.insert(entity);
} }
public FaceEntity queryByUserName(String userName) { public FaceEntity queryByUserName(String userName) {
if (TextUtils.isEmpty(userName)) { if (TextUtils.isEmpty(userName)) {
return null; return null;
} }
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); return getFaceDao().queryByUserName(userName);
return faceDao.queryByUserName(userName);
} }
public int queryFaceCount() { public int queryFaceCount() {
FaceDao faceDao = FaceDatabase.getInstance(App.getContext()).faceDao(); return getFaceDao().getFaceCount();
return faceDao.getFaceCount(); }
public List<FaceEntity> queryAllByUserName(String userName) {
return getFaceDao().queryAllByUserName(userName);
}
public FaceDao getFaceDao() {
return FaceDatabase.getInstance(App.getContext()).faceDao();
} }
} }
@@ -104,13 +104,13 @@ public interface FaceDao {
/** /**
* @return 删除临时用户人脸 * @return 删除临时用户人脸
*/ */
@Query("DELETE from face WHERE user_type = '2'") @Query("DELETE from face WHERE user_type = :userType")
int deleteTempUserFaceData(); int deleteUserFaceData(int userType);
/** /**
* 查询用户人脸数 * 查询用户人脸数
* @param userType 1-会员,2-临时用户 * @param userType 1-会员,2-临时用户
*/ */
@Query("SELECT COUNT(1) FROM face WHERE user_type = :userType") @Query("SELECT COUNT(1) FROM face WHERE user_type = :userType")
int getFaceCount(String userType); int getFaceCountByUserType(int userType);
} }
@@ -52,12 +52,39 @@ public class FaceEntity implements Parcelable {
/** /**
* 会员编号 * 会员编号
*/ */
@ColumnInfo(name = "cardNo") @ColumnInfo(name = "card_no")
private String cardNo; private String cardNo;
@Ignore @Ignore
private int trackId;//人脸追踪ID private int trackId;//人脸追踪ID
@ColumnInfo(name = "user_idd")
private String userId;
@ColumnInfo(name = "user_face_id")
private String userFaceId;
@ColumnInfo(name = "member")
private boolean member;
@ColumnInfo(name = "face_update_timestamp")
private long faceUpdateTimestamp;
public FaceEntity() {
registerTime = System.currentTimeMillis();
}
@Ignore
public FaceEntity(String userName, byte[] featureData, String userType, String cardNo, String userId, String userFaceId, boolean member, long faceUpdateTimestamp) {
this.userName = userName;
this.featureData = featureData;
this.userType = userType;
this.cardNo = cardNo;
this.userId = userId;
this.userFaceId = userFaceId;
this.member = member;
this.faceUpdateTimestamp = faceUpdateTimestamp;
registerTime = System.currentTimeMillis();
}
@Ignore
public FaceEntity(String userName, String imagePath, byte[] featureData) { public FaceEntity(String userName, String imagePath, byte[] featureData) {
this.userName = userName; this.userName = userName;
this.imagePath = imagePath; this.imagePath = imagePath;
@@ -65,6 +92,7 @@ public class FaceEntity implements Parcelable {
registerTime = System.currentTimeMillis(); registerTime = System.currentTimeMillis();
} }
@Ignore
public FaceEntity(FaceEntity faceEntity) { public FaceEntity(FaceEntity faceEntity) {
this.faceId = faceEntity.faceId; this.faceId = faceEntity.faceId;
this.userName = faceEntity.userName; this.userName = faceEntity.userName;
@@ -73,9 +101,14 @@ public class FaceEntity implements Parcelable {
this.registerTime = faceEntity.registerTime; this.registerTime = faceEntity.registerTime;
this.userType = faceEntity.getUserType(); this.userType = faceEntity.getUserType();
this.cardNo = faceEntity.getCardNo(); this.cardNo = faceEntity.getCardNo();
this.userId = faceEntity.getUserId();
this.userFaceId = faceEntity.getUserFaceId();
this.member = faceEntity.isMember();
this.faceUpdateTimestamp = faceEntity.getFaceUpdateTimestamp();
} }
@Ignore
protected FaceEntity(Parcel in) { protected FaceEntity(Parcel in) {
faceId = in.readLong(); faceId = in.readLong();
registerTime = in.readLong(); registerTime = in.readLong();
@@ -84,6 +117,10 @@ public class FaceEntity implements Parcelable {
featureData = in.createByteArray(); featureData = in.createByteArray();
userType = in.readString(); userType = in.readString();
cardNo = in.readString(); cardNo = in.readString();
userId = in.readString();
userFaceId = in.readString();
member = in.readByte() != 0;
faceUpdateTimestamp = in.readLong();
} }
public static final Creator<FaceEntity> CREATOR = new Creator<FaceEntity>() { public static final Creator<FaceEntity> CREATOR = new Creator<FaceEntity>() {
@@ -162,7 +199,37 @@ public class FaceEntity implements Parcelable {
this.cardNo = cardNo; this.cardNo = cardNo;
} }
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUserFaceId() {
return userFaceId;
}
public void setUserFaceId(String userFaceId) {
this.userFaceId = userFaceId;
}
public boolean isMember() {
return member;
}
public void setMember(boolean member) {
this.member = member;
}
public long getFaceUpdateTimestamp() {
return faceUpdateTimestamp;
}
public void setFaceUpdateTimestamp(long faceUpdateTimestamp) {
this.faceUpdateTimestamp = faceUpdateTimestamp;
}
@Override @Override
public int describeContents() { public int describeContents() {
@@ -178,6 +245,10 @@ public class FaceEntity implements Parcelable {
dest.writeByteArray(featureData); dest.writeByteArray(featureData);
dest.writeString(userType); dest.writeString(userType);
dest.writeString(cardNo); dest.writeString(cardNo);
dest.writeString(userId);
dest.writeString(userFaceId);
dest.writeByte((byte) (member ? 1 : 0));
dest.writeLong(faceUpdateTimestamp);
} }
@Override @Override
@@ -195,12 +266,16 @@ public class FaceEntity implements Parcelable {
TextUtils.equals(this.imagePath, that.imagePath) && TextUtils.equals(this.imagePath, that.imagePath) &&
Arrays.equals(featureData, that.featureData) && Arrays.equals(featureData, that.featureData) &&
TextUtils.equals(this.userType, that.userType) && TextUtils.equals(this.userType, that.userType) &&
TextUtils.equals(this.cardNo, that.cardNo); TextUtils.equals(this.cardNo, that.cardNo) &&
TextUtils.equals(this.userId, that.userId) &&
TextUtils.equals(this.userFaceId, that.userFaceId) &&
this.member == that.member &&
this.faceUpdateTimestamp == that.faceUpdateTimestamp;
} }
@Override @Override
public int hashCode() { public int hashCode() {
int result = Objects.hash(faceId, registerTime, userName, imagePath, userType, cardNo); int result = Objects.hash(faceId, registerTime, userName, imagePath, userType, cardNo, userId, userFaceId, member, faceUpdateTimestamp);
result = 31 * result + Arrays.hashCode(featureData); result = 31 * result + Arrays.hashCode(featureData);
return result; return result;
} }