refactor(api): 优化餐盘柜接口路径和数据模型

- 删除密码登录相关代码和布局资源,简化登录流程
- 修改 ApiService 接口路径由旧项目旧路径切换为新 nutrition 模块路径
- 修改设备用户信息 EquipmentUserInfo 中 id 类型由 Long 改为 String,防止精度丢失
- 重构 RemoteRepository 中绑定和解绑接口,适配新参数结构
- 优化 UserViewModel 中获取用户信息接口,添加静默请求支持
- 改进 DiagnosticExporter,优先导出到 U 盘,不可用时回落到应用目录
- UsbStorageHelper 增强 U 盘识别算法,结合路径与文件系统类型双重校验
- 移除无用的 LoginParam 请求模型以及密码登录相关引用
- 调整网络请求相关的导入语句,清理多余依赖
- 修正设备初始化 ActiveKey 的使用方式,恢复为后台下发值
- BindPlateFragment 中绑定失败提示改为“绑定成功,开柜失败”
- OpsActivity 导出诊断包按钮弹窗改为 AlertDialog 显示结果信息
- SearchParam 默认分页参数改为 pageNum=1,pageSize=10
- 修改 SettingViewModel.unbindPlate 参数类型为 String,统一接口调用参数格式
This commit is contained in:
mazengfei
2026-09-09 15:57:44 +08:00
parent 8bcc5a0289
commit 8fcbbb38f6
20 changed files with 407 additions and 295 deletions
-4
View File
@@ -47,10 +47,6 @@
android:exported="false"
android:launchMode="singleTask">
</activity>
<activity
android:name="com.sw.platecabinet.activity.LoginByPwdActivity"
android:exported="false"
android:launchMode="singleTask"/>
<activity
android:name="com.sw.platecabinet.activity.LoginByFaceActivity"
android:exported="true"
@@ -31,6 +31,7 @@ class MyApp : App() {
private fun initGlobalData() {
var deviceId = AppUtil.getUDID(this)
// deviceId = "be154831-3466-3ba2-a2ea-57652c919fed"
// deviceId="2987f0c5-5754-33e9-b00a-251db5e2e55f"
Log.d("MyApp", "initialize: deviceId=$deviceId")
GlobalData.deviceId = deviceId
@@ -50,8 +50,8 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
GlobalData.appId = deviceConfig.arcsoftAppId ?: ""
GlobalData.sdkKey = deviceConfig.arcsoftSdkKey ?: ""
// 测试设备后台下发的激活码不正确,手动硬编码覆盖为正确值(后台修复后可回退为下行)
// GlobalData.activeKey = deviceConfig.arcsoftActiveKey ?: ""
GlobalData.activeKey = "085F-118G-Q4V1-THBP"
GlobalData.activeKey = deviceConfig.arcsoftActiveKey ?: ""
// GlobalData.activeKey = "085F-118G-Q4V1-THBP"
hideWaitingDialog()
goLoginActivity()
}
@@ -111,11 +111,6 @@ class LoginByFaceActivity : BaseActivity<ActivityLoginFaceBinding>(),
//开启人脸增量数据定时任务(V2
startFaceTask()
binding.llToPwd.setOnClickListener {
val intent = Intent(this, LoginByPwdActivity::class.java)
startActivity(intent)
}
// TODO: 测试请求
TaskManager.startTask()
@@ -1,62 +0,0 @@
package com.sw.platecabinet.activity
import android.content.Intent
import com.sw.plate.utils.ToastUtils
import com.sw.platecabinet.member.databinding.ActivityLoginByPwdBinding
import com.sw.platecabinet.member.databinding.ItemTitleTimeBinding
import com.sw.platecabinet.dialog.BalanceNotEnoughDialog
import com.sw.platecabinet.model.request.LoginParam
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.utils.KeyboardUtils
/**
* 密码登录
*/
class LoginByPwdActivity : BaseActivity<ActivityLoginByPwdBinding>() {
override fun inflateViewBinding(): ActivityLoginByPwdBinding {
return ActivityLoginByPwdBinding.inflate(layoutInflater)
}
override fun inflateTitleBinding(): ItemTitleTimeBinding {
return binding.includeHeader
}
override fun initialize() {
binding.btnLogin.setOnClickListener {
val phone = binding.etPhone.text.toString()
val pwd = binding.etPwd.text.toString()
if (phone.isEmpty() || pwd.isEmpty()) {
ToastUtils.showToast("手机或校验码不能为空")
return@setOnClickListener
}
val loginParam = LoginParam(
//equipmentId = equipmentId,
phone = phone,
password = pwd
)
viewModel.loginWithPwd(loginParam)
KeyboardUtils.hideKeyboard(this)
}
binding.tvFaceRec.setOnClickListener {
val intent = Intent(this, LoginByFaceActivity::class.java)
startActivity(intent)
}
}
override fun onResume() {
super.onResume()
viewModel.resetUserInfo()
}
override fun handleLoginSuccess(equipmentUserInfo: EquipmentUserInfo, isAdmin: Boolean) {
super.handleLoginSuccess(equipmentUserInfo, isAdmin)
// val cardBalance = equipmentUserInfo.cardBalance?:0.toDouble()
// val balanceIsNotEnough = cardBalance <= 0.toDouble()
if (equipmentUserInfo.isIntercept) {
return
}
LoginByFaceActivity.goInitActivity()
finish()
}
}
@@ -283,8 +283,14 @@ class OpsActivity : BaseActivity<ActivityOpsBinding>() {
binding.btnExport.setOnClickListener {
lifecycleScope.launch(Dispatchers.IO) {
val result = DiagnosticExporter.exportToUsb(this@OpsActivity)
withContext(Dispatchers.Main) { ToastUtils.showToast(result.message) }
val result = DiagnosticExporter.export(this@OpsActivity)
withContext(Dispatchers.Main) {
AlertDialog.Builder(this@OpsActivity)
.setTitle(if (result.success) "导出成功" else "导出失败")
.setMessage(result.message)
.setPositiveButton("确定", null)
.show()
}
}
}
@@ -262,7 +262,7 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
//TODO 调用接口查询用户是否已绑盘
this@BindPlateFragment.equipmentBoxCode = null
(activity as? MainActivity)?.showWaitingDialog("查询中,请稍后……")
userViewModel.getUserInfoById(memberId = item.faceId) {
userViewModel.getUserInfoById(memberId = item.faceId, silent = true) {
(activity as? MainActivity)?.hideWaitingDialog()
this@BindPlateFragment.equipmentBoxCode = it?.equipmentBoxCode
}
@@ -306,7 +306,10 @@ class BindPlateFragment : BaseFragment<FragmentBindPlateBinding>(
}
override fun onFail(e: Exception?) {
ToastUtils.showToast("绑定失败")
// 走到这里说明 plateBinding 接口已成功(code=00000),
// 只是开柜动作失败,不能误报「绑定失败」
ToastUtils.showToast("绑定成功,开柜失败")
activity?.finish()
}
})
// }
@@ -1,30 +0,0 @@
package com.sw.platecabinet.model.request
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
/**
* 登录参数
*/
@Parcelize
data class LoginParam(
/**
* 设备Id
*/
val equipmentId: Int? = null,
/**
* 会员信息
*/
// val memberId: String? = null,
val faceId: String? = null,
/**
* 密码
*/
val password: String? = null,
/**
* 手机号
*/
val phone: String? = null
) : Parcelable
@@ -26,9 +26,9 @@ data class SearchParam(
// @SerializedName("memberFrom")
// val memberFrom: Int? = 0,
// @SerializedName("pageNum")
var pageNum: Int? = 0,
var pageNum: Int? = 1,
// @SerializedName("pageSize")
var pageSize: Int? = 0,
var pageSize: Int? = 10,
// @SerializedName("param")
// val `param`: String? = "",
var name:String?=null,
@@ -5,6 +5,7 @@ data class ApiResponse<T>(
// val success: Boolean? = false,
val msg: String? = "",
val data: T? = null,
val total: Int = 0,
// val result: T? = null,
) {
// fun isSuccess(): Boolean = code == 200
@@ -12,9 +12,9 @@ import kotlinx.parcelize.Parcelize
@Parcelize
data class EquipmentUserInfo(
/**
* 主键
* 主键(绑定记录id,后端序列化为字符串)
*/
val id: Long? = 0,
val id: String? = "",
/**
*
* 设备Id
@@ -4,7 +4,6 @@ import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.model.DeviceConfig
import com.sw.platecabinet.model.request.BindParam
import com.sw.platecabinet.model.request.EquipmentParam
import com.sw.platecabinet.model.request.LoginParam
import com.sw.platecabinet.model.request.SearchParam
import com.sw.platecabinet.model.response.ApiResponse
import com.sw.platecabinet.model.response.EquipmentInfo
@@ -12,8 +11,6 @@ import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.SearchResult
import com.sw.platecabinet.model.response.UserFaceModel
import retrofit2.http.Body
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.POST
@@ -75,13 +72,13 @@ interface ApiService {
// @Body param: LoginParam
// ): ApiResponse<EquipmentUserInfo>
/**
* 餐盘用户信息获取
/**
* 通过用户ID获取信息(人脸识别匹配到 userId 后调用,替代旧 getPlateBoxUserInfo 登录接口)
*/
@POST//("/shuwei-zhct/swEquipmentRelUser/equipmentBoxLogin")
suspend fun equipmentBoxLogin(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/sideboard/app/getPlateBoxUserInfo",
@Body param: LoginParam
@GET
suspend fun getMemberRefPlateByUserId(
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/sideboard/app/getMemberRefPlateByUserId",
@Query("userId") userId: String
): ApiResponse<EquipmentUserInfo>
/**
@@ -95,7 +92,7 @@ interface ApiService {
@GET
suspend fun getEquipmentList(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/sideboard/app/getYxMemberRefPlateByEquipmentCode",
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/sideboard/app/getYxMemberRefPlateByEquipmentCode",
): ApiResponse<List<EquipmentUserInfo>>
// /**
// * 餐盘用户信息绑定解绑
@@ -111,18 +108,17 @@ interface ApiService {
*/
@POST
suspend fun plateBind(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/sideboard/app/plateBinding",
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/sideboard/app/plateBinding",
@Body param: BindParam
): ApiResponse<EquipmentUserInfo?>
/**
* 餐盘解绑
* 餐盘解绑JSON 请求体)
*/
@POST
@FormUrlEncoded
suspend fun plateUnbind(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/sideboard/app/plateUnbind",
@Field("id") id: Long?
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/sideboard/app/plateUnbind",
@Body param: Map<String, String>
): ApiResponse<Any?>
/**
@@ -131,7 +127,7 @@ interface ApiService {
@POST//("/shuwei-user/swclientUserInfoShop/selectList")
suspend fun searchUser(
// @Url url: String = "${GlobalData.appBaseUrl}/shuwei-user/swclientUserInfoShop/selectList",
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/common/app/getUserInfoByNameOrPhone",
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/common/app/getUserInfoByNameOrPhone",
@Body param: SearchParam
): ApiResponse<List<SearchResult.Member>?>
@@ -150,7 +146,7 @@ interface ApiService {
*/
@GET
suspend fun findByPlateNumber(
@Url url: String = "${GlobalData.appBaseUrl}/terminal/neglect/sideboard/app/getMemberRefPlateByPlateNumber",
@Url url: String = "${GlobalData.appBaseUrl}/nutrition/neglect/sideboard/app/getMemberRefPlateByPlateNumber",
@Query("plateNumber") plateNumber: String
): ApiResponse<EquipmentUserInfo>
@@ -2,7 +2,6 @@ package com.sw.platecabinet.repository
import com.sw.platecabinet.model.DeviceConfig
import com.sw.platecabinet.model.request.BindParam
import com.sw.platecabinet.model.request.LoginParam
import com.sw.platecabinet.model.request.SearchParam
import com.sw.platecabinet.model.response.ApiResponse
import com.sw.platecabinet.model.response.EquipmentUserInfo
@@ -71,10 +70,10 @@ class RemoteRepository constructor(
}
/**
* 登录
* 通过用户ID获取信息(人脸识别匹配到 userId 后调用)
*/
suspend fun equipmentBoxLogin(param: LoginParam): ApiResponse<EquipmentUserInfo> {
return safeApiCall { apiService.equipmentBoxLogin(param = param) }
suspend fun getMemberRefPlateByUserId(userId: String?): ApiResponse<EquipmentUserInfo> {
return safeApiCall { apiService.getMemberRefPlateByUserId(userId = userId ?: "") }
}
/**
@@ -95,8 +94,8 @@ class RemoteRepository constructor(
return safeApiCall { apiService.plateBind(param = param) }
}
suspend fun plateUnbind(id: Long?): ApiResponse<Any?> {
return safeApiCall { apiService.plateUnbind(id = id) }
suspend fun plateUnbind(id: String?): ApiResponse<Any?> {
return safeApiCall { apiService.plateUnbind(param = mapOf("id" to (id ?: ""))) }
}
/**
@@ -5,6 +5,7 @@ import com.sw.plate.utils.arcface.FaceApi
import com.sw.plate.utils.comn.SerialApi
import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.mqtt.MqttManager
import timber.log.Timber
import java.io.BufferedOutputStream
import java.io.File
import java.io.FileOutputStream
@@ -15,8 +16,9 @@ import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
/**
* 诊断包导出:把设备信息、运行日志、崩溃日志打包成 zip,直接写入 U 盘
* 工控机现场无微信等分享应用,故不走 FileProvider 分享,改为写 U 盘
* 诊断包导出:把设备信息、运行日志、崩溃日志打包成 zip。
* 优先写 U 盘根目录;U 盘不可用或写入失败时兜底到应用 filesDir/diagnostic/
* 工控机现场无微信等分享应用,故不走 FileProvider 分享。
*/
object DiagnosticExporter {
@@ -25,13 +27,27 @@ object DiagnosticExporter {
private val faceApi = FaceApi()
/** 导出到 U 盘(在 IO 线程调用) */
fun exportToUsb(context: Context): ExportResult {
val usbDir = UsbStorageHelper.findUsbDir(context)
?: return ExportResult(false, "未检测到可写 U 盘,请插入后重试")
/**
* 导出诊断包(在 IO 线程调用):
* 1. 优先写 U 盘根目录;
* 2. U 盘不可用或写入失败时,兜底到应用 filesDir/diagnostic/,提示中给出绝对路径(便于 adb pull)。
*/
fun export(context: Context): ExportResult {
// 1. 优先尝试 U 盘
UsbStorageHelper.findUsbDir(context)?.let { usbDir ->
try {
val file = buildZip(context, usbDir)
return ExportResult(true, "已导出到 U 盘: ${file.name}", file)
} catch (_: Exception) {
// U 盘写入失败,落入应用目录兜底
}
}
// 2. 兜底:应用目录(仅保留最新一份,避免累积占用内部存储)
val appDir = File(context.filesDir, "diagnostic").apply { mkdirs() }
appDir.listFiles { f -> f.isFile && f.name.endsWith(".zip") }?.forEach { it.delete() }
return try {
val file = buildZip(context, usbDir)
ExportResult(true, "已导出: ${file.name}", file)
val file = buildZip(context, appDir)
ExportResult(true, "U 盘不可用,已导出到应用目录:\n${file.absolutePath}", file)
} catch (e: Exception) {
ExportResult(false, "导出失败: ${e.message}")
}
@@ -41,7 +57,7 @@ object DiagnosticExporter {
private fun buildZip(context: Context, outDir: File): File {
val time = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
val zipFile = File(outDir, "diagnostic_$time.zip")
Timber.d("Building zip file: ${zipFile.absolutePath}")
ZipOutputStream(BufferedOutputStream(FileOutputStream(zipFile))).use { zos ->
// 1. 设备快照 info.txt
zos.putNextEntry(ZipEntry("info.txt"))
@@ -7,21 +7,65 @@ import java.io.File
/**
* U 盘定位:工控机现场无微信等分享应用,诊断包需直接写入 U 盘。
* 按可靠性依次尝试:StorageManager 可移动卷(API30+)→ /storage 卷目录 → /mnt 常见挂载点 → /proc/mounts。
*
* 关键教训:仅凭「vfat/exfat/ntfs」判断会误判——部分瑞芯微固件把 /oempriv 等
* OEM 私有分区也格式化成 vfat。因此必须「路径 + 文件系统」双重校验:
* 只有挂载点落在 /storage/ 下、或路径明显含 usb/udisk/sdcard 关键字,才算 U 盘;
* 根目录的 /oem /odm /vendor /system 等系统分区一律排除。
*/
object UsbStorageHelper {
fun findUsbDir(context: Context): File? {
api30Volume(context)?.let { return it }
// 1. /storage 下卷标目录(XXXX-XXXX),Android 标准可移动存储位置
storageVolumeDir()?.let { return it }
commonMountPoints().forEach { if (isWritableDir(it)) return it }
// 2. /proc/mounts 里"明显是外接存储"的可移动介质挂载点
procMountsDir()?.let { return it }
// 3. 硬编码常见挂载点兜底(路径本身含 usb/udisk/sdcard,无需额外校验路径)
commonMountPoints().forEach { if (isWritableDir(it) && isRemovableFs(it)) return it }
// 4. StorageManager removable 卷(路径 + fsType 双重校验)
api30Volume(context)?.let {
if (isWritableDir(it) && isUsbMountPoint(it.absolutePath) && isRemovableFs(it)) return it
}
return null
}
private fun isWritableDir(dir: File): Boolean = dir.exists() && dir.isDirectory && dir.canWrite()
/** API 30+ 通过 StorageManager 拿可移动卷目录(最可靠) */
/** 挂载点是否"明显是外接存储":排除根目录系统分区 */
private fun isUsbMountPoint(path: String): Boolean {
val lower = path.lowercase()
if (lower.startsWith("/oem") || lower.startsWith("/odm") ||
lower.startsWith("/vendor") || lower.startsWith("/system") ||
lower.startsWith("/product") || lower.startsWith("/system_ext") ||
lower == "/data" || lower == "/cache" || lower == "/metadata" ||
lower == "/persist" || lower == "/mnt/vendor"
) return false
return lower.startsWith("/storage/") ||
lower.startsWith("/mnt/media_rw/") ||
lower.contains("usb") || lower.contains("udisk") ||
lower.contains("sdcard") || lower.contains("external_sd")
}
/** 判断目录在 /proc/mounts 中的文件系统是否为可移动介质类型 */
private fun isRemovableFs(dir: File): Boolean {
val path = dir.absolutePath
return try {
File("/proc/mounts").readLines().any { line ->
val parts = line.split(" ")
parts.size >= 3 &&
parts[1].replace("\\040", " ") == path &&
isRemovableFsType(parts[2])
}
} catch (e: Exception) {
false
}
}
private fun isRemovableFsType(fsType: String): Boolean =
fsType.contains("vfat") || fsType.contains("exfat") ||
fsType.contains("ntfs") || fsType.contains("fuseblk")
/** API 30+ 通过 StorageManager 拿可移动卷目录(不可靠,需路径 + fsType 双重佐证) */
private fun api30Volume(context: Context): File? {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return null
return try {
@@ -32,27 +76,28 @@ object UsbStorageHelper {
}
}
/** /storage 下非 emulated/self 的目录(U 盘卷形如 /storage/ABCD-1234 */
/** /storage 下卷标目录(U 盘卷形如 /storage/ABCD-1234 */
private fun storageVolumeDir(): File? {
return try {
File("/storage").listFiles()
?.filter { it.isDirectory && it.name != "emulated" && it.name != "self" && it.canWrite() }
?.firstOrNull()
?.firstOrNull { isRemovableFs(it) }
} catch (e: Exception) {
null
}
}
/** 常见挂载点(不同 Android 版本/固件差异大,做兜底 */
/** 常见挂载点(路径本身即含 usb/udisk/sdcard 关键字 */
private fun commonMountPoints(): List<File> = listOf(
File("/mnt/usb_storage"),
File("/mnt/usb"),
File("/mnt/udisk"),
File("/storage/usb"),
File("/mnt/external_sd"),
File("/mnt/sdcard2")
)
/** 读 /proc/mounts 找可移动介质挂载点vfat/exfat/ntfs/fuseblk */
/** 读 /proc/mounts 找"明显是外接存储"的可移动介质挂载点 */
private fun procMountsDir(): File? {
return try {
File("/proc/mounts").readLines()
@@ -61,9 +106,7 @@ object UsbStorageHelper {
if (parts.size < 3) return@mapNotNull null
val fsType = parts[2]
val mountPoint = parts[1].replace("\\040", " ")
if (fsType.contains("vfat") || fsType.contains("exfat") ||
fsType.contains("ntfs") || fsType.contains("fuseblk")
) File(mountPoint) else null
if (isRemovableFsType(fsType) && isUsbMountPoint(mountPoint)) File(mountPoint) else null
}
.firstOrNull { isWritableDir(it) }
} catch (e: Exception) {
@@ -130,7 +130,7 @@ class NetViewModelV2 : ViewModel() {
GlobalData.activeKey,
object : FaceApi.ActiveCallback {
override fun onSuccess(activeCode: Int) {
Timber.d("activeEngine activeCode = $activeCode")
Timber.d("activeEngine activeCode = $activeCode=="+GlobalData.activeKey)
// ThreadUtils.launch {
viewModelScope.launch(Dispatchers.Main) {
when (activeCode) {
@@ -158,7 +158,7 @@ class SettingViewModel : BaseViewModel() {
// }
// }
fun unbindPlate(id: Long?, block:(Pair<Boolean?, ErrorInfo>)-> Unit) {
fun unbindPlate(id: String?, block:(Pair<Boolean?, ErrorInfo>)-> Unit) {
block(null to ErrorInfo())
launchWithLoading {
val response = repository.plateUnbind(id)
@@ -12,7 +12,6 @@ import com.sw.platecabinet.GlobalData
import com.sw.platecabinet.GlobalData.globalEquipmentCode
import com.sw.platecabinet.GlobalKey
import com.sw.platecabinet.model.DeviceConfig
import com.sw.platecabinet.model.request.LoginParam
import com.sw.platecabinet.model.response.EquipmentUserInfo
import com.sw.platecabinet.model.response.UserFaceModel
import com.sw.platecabinet.utils.SpTool
@@ -93,29 +92,29 @@ class UserViewModel : BaseViewModel() {
private var faceTimestamp = 0L
/**
* 校验码登录
*/
fun loginWithPwd(loginParam: LoginParam) {
launchWithLoading {
val response = repository.equipmentBoxLogin(loginParam)
if (parseResponse(response)) {
_currentUserInfo.value = response.data
}
}
}
/**
* 通过用户id获取用户信息
*
* 无论成功或失败(如「未找到用户绑定信息」)都会回调 action,
* 以便调用方统一收尾(例如关闭 loading 弹窗)。
* 成功时回调绑定的用户信息,失败时回调 null。
*
* @param silent true 表示失败时不弹错误 toast。绑定页点会员检查是否已绑定
* 时,「未找到绑定信息」属于正常态,应静默处理。
*/
fun getUserInfoById(memberId: String?, action:(EquipmentUserInfo?)->Unit={}) {
fun getUserInfoById(memberId: String?, silent: Boolean = false, action:(EquipmentUserInfo?)->Unit={}) {
_currentUserInfo.value = null
launchWithLoading {
val loginParam = LoginParam(faceId = memberId)
val response = repository.equipmentBoxLogin(loginParam)
if (parseResponse(response)) {
val response = repository.getMemberRefPlateByUserId(userId = memberId)
if (response.isSuccess()) {
_currentUserInfo.value = response.data
action(response.data)
} else {
if (!silent) {
Timber.d("msg = ${response.msg}, code = ${response.code}")
ToastUtils.showToast("${response.msg}(${response.code})")
}
action(null)
}
}
}
@@ -1,122 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bg"
android:orientation="vertical">
<include
android:id="@+id/includeHeader"
layout="@layout/item_title_time" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:paddingStart="60dp"
android:paddingEnd="60dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="手机后四位"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginTop="23dp"
android:background="@drawable/shape_pwd_bg"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="55dp"
android:src="@drawable/ic_phone" />
<EditText
android:id="@+id/et_phone"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="20dp"
android:background="@android:color/transparent"
android:hint="输入手机后四位"
android:imeOptions="actionNext"
android:inputType="number"
android:maxLength="4"
android:maxLines="1"
android:textColor="#F3D2BD"
android:textColorHint="#7B6D6A"
android:textSize="26sp"
android:textStyle="bold" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:text="校验码"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginTop="23dp"
android:background="@drawable/shape_pwd_bg"
android:gravity="center_vertical">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="55dp"
android:src="@drawable/ic_pwd" />
<EditText
android:id="@+id/et_pwd"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="20dp"
android:background="@android:color/transparent"
android:hint="输入校验码"
android:inputType="numberPassword"
android:maxLines="1"
android:textColor="#F3D2BD"
android:textColorHint="#7B6D6A"
android:imeOptions="actionDone"
android:textSize="26sp"
android:textStyle="bold" />
</LinearLayout>
<android.widget.Button
android:id="@+id/btnLogin"
android:layout_width="match_parent"
android:layout_height="80dp"
android:layout_marginTop="46dp"
android:background="@drawable/shape_btn_bg"
android:text="登录"
android:textColor="#11111C"
android:textSize="30sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tvFaceRec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="40dp"
android:padding="8dp"
android:text="人脸识别"
android:textColor="#FFCC99"
android:textSize="26sp"
android:textStyle="bold" />
</LinearLayout>
</LinearLayout>