feat(environment): 新增环境切换功能及相关优化
- 新增业务环境选择弹窗 EnvironmentSelectDialog,支持本地/测试/生产环境切换 - 实现连点三次屏幕触发环境切换弹窗功能,便于快速切换接口环境 - 切换环境时清空本地人脸库并重置增量同步状态,保证环境切换的完整生效 - 业务全局 BaseUrl 支持从持久化读取,首次无值时默认测试环境 - Retrofit Client 接口 BaseUrl 优化,自动规范尾部斜杠,保证 BaseUrl 合法 - 设备初始化界面增强,增加等待框触摸穿透,优化接口请求失败时的提示信息 - 提升人脸缓存拉取接口分页大小,默认请求 500 条数据 - 修正登录界面布局细节,隐藏密码切换按钮,调整控件顺序及样式细节
This commit is contained in:
@@ -21,6 +21,7 @@ object GlobalData {
|
||||
* 竖排数量
|
||||
*/
|
||||
var arrayVertical: Int = 11
|
||||
|
||||
/**
|
||||
* 排列方式 0 垂直 1 水平
|
||||
*/
|
||||
@@ -38,9 +39,9 @@ object GlobalData {
|
||||
/**
|
||||
* 具体业务 BaseUrl
|
||||
*/
|
||||
const val LOCAL_BASE_URL: String = "http://192.168.10.101:24801"
|
||||
const val TEST_BASE_URL: String = "https://dev.yixiong-tech.com:8081"
|
||||
const val PROD_BASE_URL: String = "https://platform-api.uat.shuziweidao.com"
|
||||
const val LOCAL_BASE_URL: String = "http://192.168.10.101:24801"//开发环境
|
||||
const val TEST_BASE_URL: String = "https://dev.yixiong-tech.com:8081"//测试环境
|
||||
const val PROD_BASE_URL: String = "https://platform-api.uat.shuziweidao.com"//生产环境
|
||||
|
||||
/**
|
||||
* 设备id
|
||||
@@ -69,4 +70,23 @@ object GlobalKey {
|
||||
const val KEY_USER_INFO = "userInfoKey"
|
||||
const val PARAM_EQUIPMENT_INFO = "equipmentUserInfo"
|
||||
const val KEY_BASE_URL = "baseUrlKey"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 业务环境配置项
|
||||
* @param name 环境名称(用于弹窗展示)
|
||||
* @param url 环境 BaseUrl(不带尾斜杠)
|
||||
*/
|
||||
data class Environment(
|
||||
val name: String,
|
||||
val url: String
|
||||
)
|
||||
|
||||
/**
|
||||
* 预设环境列表(本地 / 测试 / 生产)
|
||||
*/
|
||||
val ENVIRONMENTS: List<Environment> = listOf(
|
||||
Environment("本地", GlobalData.LOCAL_BASE_URL),
|
||||
Environment("测试", GlobalData.TEST_BASE_URL),
|
||||
Environment("生产", GlobalData.PROD_BASE_URL)
|
||||
)
|
||||
@@ -4,6 +4,7 @@ import android.util.Log
|
||||
import com.sw.plate.App
|
||||
import com.sw.plate.utils.AppUtil
|
||||
import com.sw.platecabinet.utils.CrashHandler
|
||||
import com.sw.platecabinet.utils.SpTool
|
||||
import timber.log.Timber
|
||||
|
||||
class MyApp : App() {
|
||||
@@ -33,6 +34,10 @@ class MyApp : App() {
|
||||
GlobalData.appVersion = AppUtil.getAppVersionCode(this).toString()
|
||||
GlobalData.globalEquipmentCode = "202501171634"
|
||||
|
||||
// 从持久化读取业务 BaseUrl,首次无值兜底测试环境
|
||||
GlobalData.appBaseUrl = SpTool.getBaseUrl().ifBlank { GlobalData.TEST_BASE_URL }
|
||||
Timber.d("initGlobalData appBaseUrl=${GlobalData.appBaseUrl}")
|
||||
|
||||
// GlobalData.appBaseUrl = "http://192.168.1.201:14801"
|
||||
}
|
||||
}
|
||||
@@ -1,17 +1,34 @@
|
||||
package com.sw.platecabinet.activity
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.MotionEvent
|
||||
import android.view.WindowManager
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.sw.plate.utils.ToastUtils
|
||||
import com.sw.plate.utils.arcface.facedb.FaceDatabase
|
||||
import com.sw.platecabinet.Environment
|
||||
import com.sw.platecabinet.GlobalData
|
||||
import com.sw.platecabinet.dialog.EnvironmentSelectDialog
|
||||
import com.sw.platecabinet.member.databinding.ActivityDeviceInitBinding
|
||||
import com.sw.platecabinet.member.databinding.ItemTitleTimeBinding
|
||||
import com.sw.platecabinet.utils.SpTool
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* 设备初始化界面
|
||||
*/
|
||||
class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
||||
|
||||
// 连点切换环境的计数与复位
|
||||
private var tapCount = 0
|
||||
private val tapHandler = Handler(Looper.getMainLooper())
|
||||
private val resetTapRunnable = Runnable { tapCount = 0 }
|
||||
|
||||
override fun inflateViewBinding(): ActivityDeviceInitBinding {
|
||||
return ActivityDeviceInitBinding.inflate(layoutInflater)
|
||||
}
|
||||
@@ -21,7 +38,8 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
||||
}
|
||||
|
||||
override fun initialize() {
|
||||
showWaitingDialog("加载中……")
|
||||
// 等待框设为不拦截触摸,确保连点手势可达
|
||||
showWaitingDialog("加载中……")?.window?.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE)
|
||||
// 首次启动先全量拉取人脸缓存,后续走增量同步
|
||||
if (SpTool.getFirstGetFace()) {
|
||||
netViewModelV2.getUserFaceCache { status, msg ->
|
||||
@@ -33,6 +51,50 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 连续点击 3 次(500ms 内)触发环境切换弹窗
|
||||
*/
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
if (ev.action == MotionEvent.ACTION_DOWN) {
|
||||
tapCount++
|
||||
tapHandler.removeCallbacks(resetTapRunnable)
|
||||
tapHandler.postDelayed(resetTapRunnable, 500)
|
||||
if (tapCount >= 3) {
|
||||
tapCount = 0
|
||||
tapHandler.removeCallbacks(resetTapRunnable)
|
||||
showEnvironmentDialog()
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出环境选择弹窗
|
||||
*/
|
||||
private fun showEnvironmentDialog() {
|
||||
EnvironmentSelectDialog(this) { env ->
|
||||
switchEnvironment(env)
|
||||
}.show()
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换环境:持久化 + 清空本地人脸库 + 重置增量同步状态
|
||||
*/
|
||||
private fun switchEnvironment(env: Environment) {
|
||||
SpTool.setBaseUrl(env.url)
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val faceDao = FaceDatabase.getInstance(this@DeviceInitActivity).faceDao()
|
||||
faceDao.deleteAll()
|
||||
faceDao.resetId()
|
||||
SpTool.setLastFaceTimestamp(0L)
|
||||
SpTool.setFirstGetFace(true)
|
||||
withContext(Dispatchers.Main) {
|
||||
ToastUtils.showToast("已切换到${env.name},重启后生效")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设备配置(V2),并初始化虹软 SDK 激活参数
|
||||
*/
|
||||
@@ -41,8 +103,8 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
||||
runOnUiThread {
|
||||
if (deviceConfig == null) {
|
||||
hideWaitingDialog()
|
||||
ToastUtils.showToast("获取设备配置数据失败")
|
||||
goLoginActivity()
|
||||
ToastUtils.showToast("获取设备配置失败,请连点屏幕3次切换环境")
|
||||
// goLoginActivity()
|
||||
return@runOnUiThread
|
||||
}
|
||||
GlobalData.appId = deviceConfig.arcsoftAppId ?: ""
|
||||
@@ -56,8 +118,9 @@ class DeviceInitActivity : BaseActivity<ActivityDeviceInitBinding>() {
|
||||
}, onFailure = { errMsg ->
|
||||
runOnUiThread {
|
||||
hideWaitingDialog()
|
||||
ToastUtils.showToast(errMsg ?: "接口异常")
|
||||
goLoginActivity()
|
||||
Timber.e("getDeviceConfig onFailure: $errMsg")
|
||||
ToastUtils.showToast("服务连接失败,请连点屏幕3次切换环境")
|
||||
// goLoginActivity()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.sw.platecabinet.dialog
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.view.Window
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.RadioButton
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.sw.platecabinet.ENVIRONMENTS
|
||||
import com.sw.platecabinet.Environment
|
||||
import com.sw.platecabinet.GlobalData
|
||||
import com.sw.platecabinet.ext.dp
|
||||
import com.sw.platecabinet.member.databinding.DialogEnvironmentSelectBinding
|
||||
|
||||
/**
|
||||
* 业务环境选择弹窗
|
||||
*/
|
||||
class EnvironmentSelectDialog(
|
||||
private val activity: FragmentActivity,
|
||||
private val onConfirm: (Environment) -> Unit
|
||||
) : Dialog(activity) {
|
||||
|
||||
private lateinit var binding: DialogEnvironmentSelectBinding
|
||||
private var selected: Environment? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
requestWindowFeature(Window.FEATURE_NO_TITLE)
|
||||
binding = DialogEnvironmentSelectBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
window?.run {
|
||||
attributes = attributes.apply { width = 480.dp }
|
||||
setBackgroundDrawable(ColorDrawable())
|
||||
setCancelable(true)
|
||||
}
|
||||
renderEnvironments()
|
||||
addListener()
|
||||
}
|
||||
|
||||
/**
|
||||
* 渲染环境选项,高亮当前环境
|
||||
*/
|
||||
private fun renderEnvironments() {
|
||||
val current = GlobalData.appBaseUrl
|
||||
ENVIRONMENTS.forEach { env ->
|
||||
val rb = RadioButton(activity).apply {
|
||||
id = View.generateViewId()
|
||||
text = env.name
|
||||
gravity = Gravity.CENTER_VERTICAL
|
||||
textSize = 18f
|
||||
setTextColor(Color.WHITE)
|
||||
buttonTintList = ColorStateList.valueOf(Color.WHITE)
|
||||
isChecked = env.url == current
|
||||
setOnClickListener { selected = env }
|
||||
}
|
||||
binding.rgEnvironments.addView(
|
||||
rb,
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
).apply { topMargin = 8.dp }
|
||||
)
|
||||
}
|
||||
selected = ENVIRONMENTS.firstOrNull { it.url == current }
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认按钮:回调所选环境并关闭弹窗
|
||||
*/
|
||||
private fun addListener() {
|
||||
binding.tvConfirm.setOnClickListener {
|
||||
selected?.let(onConfirm)
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ object ApiClient {
|
||||
|
||||
private val retrofit by lazy {
|
||||
Retrofit.Builder()
|
||||
.baseUrl(GlobalData.appBaseUrl)
|
||||
.baseUrl(resolveBaseUrl())
|
||||
.client(okHttpClient)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build()
|
||||
@@ -42,4 +42,13 @@ object ApiClient {
|
||||
val apiServiceV2: ApiServiceV2 by lazy {
|
||||
retrofit.create(ApiServiceV2::class.java)
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化 Retrofit 要求的 baseUrl:非空且以 / 结尾。
|
||||
* 所有接口走 @Url 全路径,此值不参与实际拼接,只需合法。
|
||||
*/
|
||||
private fun resolveBaseUrl(): String {
|
||||
val url = GlobalData.appBaseUrl.ifBlank { "http://localhost/" }
|
||||
return if (url.endsWith("/")) url else "$url/"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ class RemoteRepositoryV2 constructor(
|
||||
*/
|
||||
suspend fun getUserFaceCache(
|
||||
pageNum: Int,
|
||||
pageSize: Int = 100,
|
||||
pageSize: Int = 500,
|
||||
): ApiResponse<List<UserFaceModelV2>> {
|
||||
return safeApiCall {
|
||||
apiService.getUserFaceCache(
|
||||
|
||||
Reference in New Issue
Block a user