From 04b138d3836e03c9adbcbd367fd71d92905c5206 Mon Sep 17 00:00:00 2001
From: cloudroam <cloudroam>
Date: 星期四, 17 四月 2025 13:17:45 +0800
Subject: [PATCH] add: 重复登录处理
---
app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt | 43 ++++
app/src/main/java/com/example/firstapp/adapter/ExpressAdapter.kt | 52 +++++
app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt | 32 +++
app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt | 43 ++++
app/src/main/java/com/example/firstapp/network/TokenExpiredInterceptor.kt | 45 ++++
app/src/main/java/com/example/firstapp/database/service/ApiService.kt | 47 +++-
app/src/main/java/com/example/firstapp/network/ResponseInterceptor.kt | 85 +++++++++
app/src/main/java/com/example/firstapp/adapter/FinanceAdapter.kt | 52 +++++
app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt | 138 +++++++++++---
9 files changed, 459 insertions(+), 78 deletions(-)
diff --git a/app/src/main/java/com/example/firstapp/adapter/ExpressAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/ExpressAdapter.kt
index 6bffe16..0c74eb3 100644
--- a/app/src/main/java/com/example/firstapp/adapter/ExpressAdapter.kt
+++ b/app/src/main/java/com/example/firstapp/adapter/ExpressAdapter.kt
@@ -44,9 +44,29 @@
private var currentGroup: ExpressGroup? = null
init {
+ // 设置固定高度和禁用嵌套滚动来解决滑动问题
binding.rvPackages.apply {
- layoutManager = LinearLayoutManager(context)
+ layoutManager = object : LinearLayoutManager(context) {
+ override fun canScrollVertically(): Boolean {
+ // 禁用内部RecyclerView的垂直滚动
+ return false
+ }
+
+ // 确保测量所有子项,防止部分内容不可见
+ override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
+ try {
+ super.onLayoutChildren(recycler, state)
+ } catch (e: IndexOutOfBoundsException) {
+ // 捕获可能的异常,防止崩溃
+ }
+ }
+ }
adapter = packagesAdapter
+ // 禁用嵌套滚动,让外部RecyclerView处理所有滚动
+ isNestedScrollingEnabled = false
+ // 启用回收视图缓存
+ setItemViewCacheSize(20)
+ setHasFixedSize(true)
}
}
@@ -54,7 +74,13 @@
currentGroup = group
binding.tvStationName.text = group.stationName
binding.tvPackageCount.text = "共${group.packages.size}个包裹"
+
+ // 确保所有数据都被更新
+ packagesAdapter.submitList(null)
packagesAdapter.submitList(group.packages)
+
+ // 请求布局刷新
+ binding.rvPackages.requestLayout()
}
fun setOnPackageClickListener(listener: (ExpressGroup, ExpressPackage) -> Unit) {
@@ -80,13 +106,21 @@
holder.bind(pack)
}
+ // 防止部分内容不显示
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemExpressPackageHomeBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackageClick(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackageClick(pack)
+ }
}
}
@@ -125,13 +159,21 @@
holder.bind(pack)
}
+ // 防止部分内容不显示
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemPackageBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.ivPackageStatus.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackagePickup(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackagePickup(pack)
+ }
}
binding.root.setOnClickListener(null)
diff --git a/app/src/main/java/com/example/firstapp/adapter/FinanceAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/FinanceAdapter.kt
index b0e39a4..16bbf8d 100644
--- a/app/src/main/java/com/example/firstapp/adapter/FinanceAdapter.kt
+++ b/app/src/main/java/com/example/firstapp/adapter/FinanceAdapter.kt
@@ -46,9 +46,29 @@
private var currentGroup: FinanceGroup? = null
init {
+ // 设置固定高度和禁用嵌套滚动来解决滑动问题
binding.rvPackages.apply {
- layoutManager = LinearLayoutManager(context)
+ layoutManager = object : LinearLayoutManager(context) {
+ override fun canScrollVertically(): Boolean {
+ // 禁用内部RecyclerView的垂直滚动
+ return false
+ }
+
+ // 确保测量所有子项,防止部分内容不可见
+ override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
+ try {
+ super.onLayoutChildren(recycler, state)
+ } catch (e: IndexOutOfBoundsException) {
+ // 捕获可能的异常,防止崩溃
+ }
+ }
+ }
adapter = packagesAdapter
+ // 禁用嵌套滚动,让外部RecyclerView处理所有滚动
+ isNestedScrollingEnabled = false
+ // 启用回收视图缓存
+ setItemViewCacheSize(20)
+ setHasFixedSize(true)
}
}
@@ -56,7 +76,13 @@
currentGroup = group
binding.tvStationName.text = group.stationName
binding.tvPackageCount.text = "共${group.packages.size}笔账单"
+
+ // 确保所有数据都被更新
+ packagesAdapter.submitList(null)
packagesAdapter.submitList(group.packages)
+
+ // 请求布局刷新
+ binding.rvPackages.requestLayout()
}
fun setOnPackageClickListener(listener: (FinanceGroup, FinancePackage) -> Unit) {
@@ -81,14 +107,22 @@
val pack = getItem(position)
holder.bind(pack)
}
+
+ // 防止部分内容不显示
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
inner class ViewHolder(private val binding: ItemFinancePackageHomeBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackageClick(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackageClick(pack)
+ }
}
}
@@ -126,14 +160,22 @@
val pack = getItem(position)
holder.bind(pack)
}
+
+ // 防止部分内容不显示
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
inner class ViewHolder(private val binding: ItemFinanceBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.ivPackageStatus.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackagePickup(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackagePickup(pack)
+ }
}
binding.root.setOnClickListener(null)
diff --git a/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt
index 2c1365f..e39c845 100644
--- a/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt
+++ b/app/src/main/java/com/example/firstapp/adapter/FlightAdapter.kt
@@ -47,8 +47,23 @@
init {
binding.rvPackages.apply {
- layoutManager = LinearLayoutManager(context)
+ layoutManager = object : LinearLayoutManager(context) {
+ override fun canScrollVertically(): Boolean {
+ return false
+ }
+
+ override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
+ try {
+ super.onLayoutChildren(recycler, state)
+ } catch (e: IndexOutOfBoundsException) {
+ // 捕获可能的异常,防止崩溃
+ }
+ }
+ }
adapter = packagesAdapter
+ isNestedScrollingEnabled = false
+ setItemViewCacheSize(20)
+ setHasFixedSize(true)
}
}
@@ -56,7 +71,11 @@
currentGroup = group
binding.tvStationName.text = group.stationName
binding.tvPackageCount.text = "共${group.packages.size}张机票"
+
+ packagesAdapter.submitList(null)
packagesAdapter.submitList(group.packages)
+
+ binding.rvPackages.requestLayout()
}
fun setOnPackageClickListener(listener: (FlightGroup, FlightPackage) -> Unit) {
@@ -82,13 +101,20 @@
holder.bind(pack)
}
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemFlightPackageHomeBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackageClick(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackageClick(pack)
+ }
}
}
@@ -127,13 +153,20 @@
holder.bind(pack)
}
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemFlightBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.ivPackageStatus.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackagePickup(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackagePickup(pack)
+ }
}
binding.root.setOnClickListener(null)
diff --git a/app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt
index 856ba45..67ba7d7 100644
--- a/app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt
+++ b/app/src/main/java/com/example/firstapp/adapter/IncomeAdapter.kt
@@ -42,8 +42,23 @@
init {
binding.rvPackages.apply {
- layoutManager = LinearLayoutManager(context)
+ layoutManager = object : LinearLayoutManager(context) {
+ override fun canScrollVertically(): Boolean {
+ return false
+ }
+
+ override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
+ try {
+ super.onLayoutChildren(recycler, state)
+ } catch (e: IndexOutOfBoundsException) {
+ // 捕获可能的异常,防止崩溃
+ }
+ }
+ }
adapter = packagesAdapter
+ isNestedScrollingEnabled = false
+ setItemViewCacheSize(20)
+ setHasFixedSize(true)
}
}
@@ -51,7 +66,11 @@
currentGroup = group
binding.tvStationName.text = group.stationName
binding.tvPackageCount.text = "共${group.packages.size}笔收入"
+
+ packagesAdapter.submitList(null)
packagesAdapter.submitList(group.packages)
+
+ binding.rvPackages.requestLayout()
}
}
}
@@ -71,13 +90,20 @@
holder.bind(pack)
}
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemIncomePackageHomeBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackageClick(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackageClick(pack)
+ }
}
}
diff --git a/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt b/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt
index 0efd02f..fb7490a 100644
--- a/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt
+++ b/app/src/main/java/com/example/firstapp/adapter/TrainAdapter.kt
@@ -47,8 +47,23 @@
init {
binding.rvPackages.apply {
- layoutManager = LinearLayoutManager(context)
+ layoutManager = object : LinearLayoutManager(context) {
+ override fun canScrollVertically(): Boolean {
+ return false
+ }
+
+ override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) {
+ try {
+ super.onLayoutChildren(recycler, state)
+ } catch (e: IndexOutOfBoundsException) {
+ // 捕获可能的异常,防止崩溃
+ }
+ }
+ }
adapter = packagesAdapter
+ isNestedScrollingEnabled = false
+ setItemViewCacheSize(20)
+ setHasFixedSize(true)
}
}
@@ -56,7 +71,11 @@
currentGroup = group
binding.tvStationName.text = group.stationName
binding.tvPackageCount.text = "共${group.packages.size}张车票"
+
+ packagesAdapter.submitList(null)
packagesAdapter.submitList(group.packages)
+
+ binding.rvPackages.requestLayout()
}
fun setOnPackageClickListener(listener: (TrainGroup, TrainPackage) -> Unit) {
@@ -82,13 +101,20 @@
holder.bind(pack)
}
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemTrainPackageHomeBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.root.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackageClick(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackageClick(pack)
+ }
}
}
@@ -127,13 +153,20 @@
holder.bind(pack)
}
+ override fun getItemCount(): Int {
+ return currentList.size
+ }
+
inner class ViewHolder(private val binding: ItemTrainBinding) :
RecyclerView.ViewHolder(binding.root) {
init {
binding.ivPackageStatus.setOnClickListener {
- val pack = getItem(adapterPosition)
- onPackagePickup(pack)
+ val position = adapterPosition
+ if (position != RecyclerView.NO_POSITION) {
+ val pack = getItem(position)
+ onPackagePickup(pack)
+ }
}
binding.root.setOnClickListener(null)
diff --git a/app/src/main/java/com/example/firstapp/database/service/ApiService.kt b/app/src/main/java/com/example/firstapp/database/service/ApiService.kt
index 41c83c2..989348b 100644
--- a/app/src/main/java/com/example/firstapp/database/service/ApiService.kt
+++ b/app/src/main/java/com/example/firstapp/database/service/ApiService.kt
@@ -1,6 +1,7 @@
package com.example.firstapp.database.service
import TokenResponse
+import android.content.Context
import com.example.firstapp.database.entity.ApiResponse
import com.example.firstapp.database.entity.KeywordConfig
import com.example.firstapp.database.request.ProductOrdersRequest
@@ -16,6 +17,8 @@
import com.example.firstapp.model.CategoryConfig
import com.example.firstapp.model.CategoryConfigSync
import com.example.firstapp.network.AuthInterceptor
+import com.example.firstapp.network.ResponseInterceptor
+import com.example.firstapp.network.TokenExpiredInterceptor
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.RequestBody
@@ -72,9 +75,11 @@
@POST("api/account/close")
suspend fun closeAccount(): AccountCloseResponse
- fun getUserCategories(currentUserId: String): List<CategoryConfig>
+ @GET("api/categoryConfig/getByUserId/{userId}")
+ suspend fun getUserCategories(@Path("userId") currentUserId: String): List<CategoryConfig>
- fun saveUserCategories(categoryConfigSync: CategoryConfigSync)
+ @POST("api/categoryConfig/saveOrUpdate/")
+ suspend fun saveUserCategories(@Body categoryConfigSync: CategoryConfigSync)
}
@@ -84,25 +89,37 @@
// private const val BASE_URL ="http://192.168.1.213:8080/flower/"
private const val BASE_URL ="http://14.103.144.28:8080/flower/"
+ private lateinit var appContext: Context
+
+ // 初始化方法,需要在Application中调用
+ fun init(context: Context) {
+ appContext = context.applicationContext
+ }
// 创建OkHttpClient,配置拦截器和超时时间
- private val okHttpClient = OkHttpClient.Builder()
- .addInterceptor(AuthInterceptor())
- .connectTimeout(30, TimeUnit.SECONDS)
- .readTimeout(30, TimeUnit.SECONDS)
- .writeTimeout(30, TimeUnit.SECONDS)
- .build()
+ private val okHttpClient by lazy {
+ OkHttpClient.Builder()
+ .addInterceptor(AuthInterceptor())
+ .addInterceptor(TokenExpiredInterceptor(appContext))
+ .addInterceptor(ResponseInterceptor(appContext))
+ .connectTimeout(30, TimeUnit.SECONDS)
+ .readTimeout(30, TimeUnit.SECONDS)
+ .writeTimeout(30, TimeUnit.SECONDS)
+ .build()
+ }
//添加Gson解析器,用于自动将JSON响应转换为Kotlin/Java对象
- private val retrofit = Retrofit
- .Builder()
- .client(okHttpClient)
- .baseUrl(BASE_URL)
- .addConverterFactory(GsonConverterFactory.create())
- .build()
+ private val retrofit by lazy {
+ Retrofit
+ .Builder()
+ .client(okHttpClient)
+ .baseUrl(BASE_URL)
+ .addConverterFactory(GsonConverterFactory.create())
+ .build()
+ }
//通过动态代理技术创建ApiService接口的具体实现类
- val apiService:ApiService = retrofit.create(ApiService::class.java)
+ val apiService by lazy { retrofit.create(ApiService::class.java) }
}
diff --git a/app/src/main/java/com/example/firstapp/network/ResponseInterceptor.kt b/app/src/main/java/com/example/firstapp/network/ResponseInterceptor.kt
new file mode 100644
index 0000000..4c8954d
--- /dev/null
+++ b/app/src/main/java/com/example/firstapp/network/ResponseInterceptor.kt
@@ -0,0 +1,85 @@
+package com.example.firstapp.network
+
+import android.content.Context
+import android.content.Intent
+import android.widget.Toast
+import com.example.firstapp.activity.LoginActivity
+import com.example.firstapp.utils.PreferencesManager
+import com.google.gson.JsonParser
+import okhttp3.Interceptor
+import okhttp3.Response
+import okhttp3.ResponseBody
+import okio.Buffer
+import kotlin.concurrent.thread
+
+/**
+ * 响应拦截器 - 处理业务错误码
+ * 根据后端提供的接口规范,检测token失效的情况
+ */
+class ResponseInterceptor(private val context: Context) : Interceptor {
+ override fun intercept(chain: Interceptor.Chain): Response {
+ val request = chain.request()
+ val response = chain.proceed(request)
+
+ try {
+ // 只处理成功的响应
+ if (response.isSuccessful) {
+ val responseBody = response.body
+ if (responseBody != null) {
+ val source = responseBody.source()
+ source.request(Long.MAX_VALUE)
+ val buffer = source.buffer.clone()
+ val responseBodyString = buffer.readUtf8()
+
+ try {
+ // 解析JSON
+ val jsonObject = JsonParser.parseString(responseBodyString).asJsonObject
+
+ // 检查业务状态码
+ if (jsonObject.has("code")) {
+ val code = jsonObject.get("code").asString
+
+ // 如果状态码表示token失效或登录过期
+ if (code == "401" || code == "-1" || code == "1001") {
+ // 检查返回的错误消息
+ val message = if (jsonObject.has("msg")) jsonObject.get("msg").asString else "登录已失效,请重新登录"
+
+ if (message.contains("token") ||
+ message.contains("登录") ||
+ message.contains("认证") ||
+ message.contains("授权")) {
+
+ // 清除本地token
+ PreferencesManager.clearUserData()
+
+ // 在主线程中显示提示并跳转到登录页面
+ thread {
+ android.os.Handler(context.mainLooper).post {
+ Toast.makeText(context, message, Toast.LENGTH_LONG).show()
+
+ // 创建跳转到登录页面的Intent,并添加清除任务栈的标志
+ val intent = Intent(context, LoginActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ context.startActivity(intent)
+ }
+ }
+ }
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ // 因为我们已经读取了响应体,需要重新创建一个新的响应体
+ val newResponseBody = ResponseBody.create(responseBody.contentType(), responseBodyString)
+ return response.newBuilder().body(newResponseBody).build()
+ }
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+
+ return response
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/network/TokenExpiredInterceptor.kt b/app/src/main/java/com/example/firstapp/network/TokenExpiredInterceptor.kt
index 1c73d50..dcc4a2f 100644
--- a/app/src/main/java/com/example/firstapp/network/TokenExpiredInterceptor.kt
+++ b/app/src/main/java/com/example/firstapp/network/TokenExpiredInterceptor.kt
@@ -1,19 +1,54 @@
package com.example.firstapp.network
+import android.content.Context
+import android.content.Intent
+import android.widget.Toast
+import com.example.firstapp.activity.LoginActivity
import com.example.firstapp.utils.PreferencesManager
import okhttp3.Interceptor
import okhttp3.Response
+import kotlin.concurrent.thread
-class TokenExpiredInterceptor : Interceptor {
+class TokenExpiredInterceptor(private val context: Context) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
- // 如果返回401,说明token可能过期
- if (response.code == 401) {
- PreferencesManager.clearUserData() // 清除本地token
- // TODO: 处理token过期,例如跳转到登录页面
+ // 如果返回401或后端自定义的token失效状态码,说明token可能过期
+ if (response.code == 401 || isTokenInvalid(response)) {
+ // 清除本地token
+ PreferencesManager.clearUserData()
+
+ // 在主线程中显示提示并跳转到登录页面
+ thread {
+ android.os.Handler(context.mainLooper).post {
+ Toast.makeText(context, "登录已失效,请重新登录", Toast.LENGTH_LONG).show()
+
+ // 创建跳转到登录页面的Intent,并添加清除任务栈的标志
+ val intent = Intent(context, LoginActivity::class.java).apply {
+ flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+ }
+ context.startActivity(intent)
+ }
+ }
}
return response
}
+
+ // 检查响应是否表示token失效
+ private fun isTokenInvalid(response: Response): Boolean {
+ try {
+ // 尝试读取响应体,检查自定义的错误码
+ // 注意:这会消耗响应体,如果需要在后续处理中使用响应体,需要克隆
+ val responseBody = response.peekBody(4096).string()
+
+ // 根据您的后端逻辑,检查是否包含token失效的提示
+ // 这里假设后端在返回JSON中包含了错误码和消息
+ return responseBody.contains("\"code\":\"401\"") ||
+ responseBody.contains("token失效") ||
+ responseBody.contains("请重新登录")
+ } catch (e: Exception) {
+ return false
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
index 45b5b0a..b367f26 100644
--- a/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
+++ b/app/src/main/java/com/example/firstapp/ui/home/HomeViewModel.kt
@@ -50,8 +50,9 @@
val unreadReminderCount: LiveData<Int> = _unreadReminderCount
private lateinit var secureStorage: SecureStorage
- private lateinit var currentUserId: String
+ private var currentUserId: String = ""
private lateinit var reminderRecordRepository: ReminderRecordRepository
+ private var categoriesLoaded = false
init {
// 初始化时加载包裹列表数据
@@ -64,7 +65,13 @@
secureStorage = SecureStorage(context)
currentUserId = userId
reminderRecordRepository = ReminderRecordRepository(context)
- loadCategories()
+
+ // 只在首次加载或者用户修改分类后加载分类数据
+ if (!categoriesLoaded) {
+ loadCategories()
+ categoriesLoaded = true
+ }
+
// 初始化时更新可见分类
_categories.value?.let { updateVisibleCategories(it) }
// 加载未读提醒数量
@@ -188,9 +195,6 @@
private fun loadCategories() {
viewModelScope.launch {
try {
- // 先尝试从本地获取配置
- val localCategories = secureStorage.getCategories(currentUserId)
-
// 默认完整分类列表
val fullCategories = listOf(
CategoryConfig(1, "快递", 0, true),
@@ -206,30 +210,73 @@
CategoryConfig(2, "还款", 1, true)
)
- if (localCategories.isNotEmpty()) {
- // 如果本地有配置,直接使用本地配置
- _categories.value = localCategories
- } else {
- try {
- // 尝试从服务器获取用户信息判断是否是会员
- val savedPhone = PreferencesManager.getPhone()
- val response = RetrofitClient.apiService.getUserInfo(savedPhone ?: "")
- val isMember = response.code == "0" && response.data?.isMember == true
+ try {
+ // 获取会员状态
+ val savedPhone = PreferencesManager.getPhone()
+ val userResponse = RetrofitClient.apiService.getUserInfo(savedPhone ?: "")
+ val isMember = userResponse.code == "0" && userResponse.data?.isMember == true
+
+ // 从用户信息中获取正确的userId
+ if (userResponse.code == "0" && userResponse.data != null) {
+ currentUserId = userResponse.data?.id.toString()
+ }
- // 根据会员状态设置默认分类
- val defaultCategories = if (isMember) fullCategories else basicCategories
- _categories.value = defaultCategories
- secureStorage.saveCategories(currentUserId, defaultCategories)
-
- // 同步到服务器
+ // 首先检查本地是否有缓存的分类配置
+ val localCategories = secureStorage.getCategories(currentUserId)
+
+ if (localCategories.isNotEmpty()) {
+ // 使用本地缓存的配置
+ _categories.value = localCategories
+ } else {
+ // 本地无缓存,尝试从服务器获取
try {
- syncCategoriesToServer(defaultCategories)
+ val serverCategories = RetrofitClient.apiService.getUserCategories(currentUserId)
+
+ if (serverCategories.isNotEmpty()) {
+ // 服务器有配置,使用服务器配置
+ // 如果不是会员,需要过滤掉会员专属分类
+ val filteredCategories = if (isMember) {
+ serverCategories
+ } else {
+ serverCategories.filter { it.name == "快递" || it.name == "还款" }
+ }
+
+ _categories.value = filteredCategories
+ // 同时更新本地缓存
+ secureStorage.saveCategories(currentUserId, filteredCategories)
+ // 同步回服务器(如果有变化)
+ if (filteredCategories.size != serverCategories.size) {
+ syncCategoriesToServer(filteredCategories)
+ }
+ } else {
+ // 服务器返回空,根据会员状态设置默认分类
+ val defaultCategories = if (isMember) fullCategories else basicCategories
+ _categories.value = defaultCategories
+ // 更新本地缓存
+ secureStorage.saveCategories(currentUserId, defaultCategories)
+ // 同步到服务器
+ syncCategoriesToServer(defaultCategories)
+ }
} catch (e: Exception) {
- Log.e("HomeViewModel", "Failed to sync categories: ${e.message}")
+ // 服务器获取失败,使用默认分类
+ Log.e("HomeViewModel", "Failed to get categories from server: ${e.message}")
+ val defaultCategories = if (isMember) fullCategories else basicCategories
+ _categories.value = defaultCategories
+ secureStorage.saveCategories(currentUserId, defaultCategories)
}
- } catch (e: Exception) {
- // 如果获取用户信息失败,使用基础分类
+ }
+ } catch (e: Exception) {
+ // 网络连接失败,尝试从本地获取配置
+ Log.e("HomeViewModel", "Failed to get user info: ${e.message}")
+ val localCategories = secureStorage.getCategories(currentUserId)
+
+ if (localCategories.isNotEmpty()) {
+ // 使用本地缓存的配置
+ _categories.value = localCategories
+ } else {
+ // 本地也没有配置,使用基础分类
_categories.value = basicCategories
+ // 更新本地缓存
secureStorage.saveCategories(currentUserId, basicCategories)
}
}
@@ -239,20 +286,26 @@
} catch (e: Exception) {
Log.e("HomeViewModel", "Failed to load categories: ${e.message}")
+ // 出现异常时,使用基础分类
+ val basicCategories = listOf(
+ CategoryConfig(1, "快递", 0, true),
+ CategoryConfig(2, "还款", 1, true)
+ )
+ _categories.value = basicCategories
+ // 更新可见分类
+ updateVisibleCategories(basicCategories)
}
}
}
- private fun syncCategoriesToServer(categories: List<CategoryConfig>) {
- viewModelScope.launch {
- try {
- RetrofitClient.apiService.saveUserCategories(
- CategoryConfigSync(currentUserId, categories)
- )
- } catch (e: Exception) {
- // 同步失败,可以稍后重试或者显示提示
- Log.e("CategorySync", "Failed to sync categories: ${e.message}")
- }
+ private suspend fun syncCategoriesToServer(categories: List<CategoryConfig>) {
+ try {
+ RetrofitClient.apiService.saveUserCategories(
+ CategoryConfigSync(currentUserId, categories)
+ )
+ } catch (e: Exception) {
+ // 同步失败,可以稍后重试或者显示提示
+ Log.e("CategorySync", "Failed to sync categories: ${e.message}")
}
}
@@ -261,9 +314,17 @@
// 保存到本地存储
secureStorage.saveCategories(currentUserId, categories)
// 同步到服务器
- syncCategoriesToServer(categories)
+ viewModelScope.launch {
+ try {
+ syncCategoriesToServer(categories)
+ } catch (e: Exception) {
+ Log.e("HomeViewModel", "Failed to sync categories: ${e.message}")
+ }
+ }
// 更新可见分类
updateVisibleCategories(categories)
+ // 标记分类已被修改
+ categoriesLoaded = true
}
private fun updateVisibleCategories(categories: List<CategoryConfig>) {
@@ -287,10 +348,17 @@
}
}
+ // 添加方法以强制刷新分类
+ fun refreshCategories() {
+ categoriesLoaded = false
+ loadCategories()
+ }
+
// 登出时不再清除本地数据
fun logout() {
// 只清除内存中的数据
_categories.value = emptyList()
+ categoriesLoaded = false
}
}
\ No newline at end of file
--
Gitblit v1.9.3