| | |
| | | // 使用协程处理API调用和数据库操作 |
| | | CoroutineScope(Dispatchers.IO).launch { |
| | | try { |
| | | // API调用移到synchronized块外 |
| | | val response = RetrofitModelClient.modelService.processSms(mapOf("content" to messageBody)) |
| | | |
| | | // 数据库操作放在synchronized块内 |
| | | synchronized(syncLock) { |
| | | if (response.status == "success") { |
| | | when (response.data.category) { |
| | | "快递" -> { |
| | | val pickupCode = response.data.details.pickupCode ?: "" |
| | | if (pickupCode.isNotEmpty()) { |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | response.data.category, |
| | | response.data.details.pickupCode ?: "", |
| | | pickupCode, |
| | | dateString |
| | | ) |
| | | |
| | |
| | | createTime = dateString, |
| | | oneLevel = response.data.details.post ?: "", |
| | | secondLevel = response.data.details.company ?: "", |
| | | code = response.data.details.pickupCode ?: "", |
| | | code = pickupCode, |
| | | pickup = 0, |
| | | pickupTime = "", |
| | | overTime = "", |
| | | address = response.data.details.address ?: "", |
| | | remarks = response.data.details.time ?: "", |
| | | ) |
| | | if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { |
| | | if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) { |
| | | Core.code.insert(code) |
| | | android.util.Log.d("MainActivity", "历史快递短信已保存: $pickupCode") |
| | | } |
| | | android.util.Log.d("MainActivity", "历史快递短信已保存: ${response.data.details.pickupCode}") |
| | | } else { |
| | | android.util.Log.d("MainActivity", "发现重复快递短信,跳过保存: ${response.data.details.pickupCode}") |
| | | android.util.Log.d("MainActivity", "发现重复快递短信,跳过保存: $pickupCode") |
| | | } |
| | | } |
| | | } |
| | | "还款" -> { |
| | | val amount = response.data.details.amount ?: "" |
| | | if (amount.isNotEmpty()) { |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | response.data.category, |
| | | response.data.details.amount ?: "", |
| | | amount, |
| | | dateString |
| | | ) |
| | | |
| | |
| | | createTime = dateString, |
| | | oneLevel = response.data.details.type ?: "", |
| | | secondLevel = response.data.details.bank ?: "", |
| | | code = response.data.details.amount ?: "", |
| | | code = amount, |
| | | pickup = 0, |
| | | pickupTime = "", |
| | | overTime = response.data.details.date ?: "", |
| | | address = response.data.details.address ?: "", |
| | | remarks = "最小还款金额${response.data.details.min_amount}还款卡号${response.data.details.number}" |
| | | ) |
| | | if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { |
| | | if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) { |
| | | Core.code.insert(code) |
| | | android.util.Log.d("MainActivity", "历史还款短信已保存: $amount") |
| | | } |
| | | android.util.Log.d("MainActivity", "历史还款短信已保存: ${response.data.details.amount}") |
| | | } else { |
| | | android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: ${response.data.details.amount}") |
| | | android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: $amount") |
| | | } |
| | | } |
| | | |
| | | } |
| | | "收入" -> { |
| | | val amount = response.data.details.amount ?: "" |
| | | if (amount.isNotEmpty()) { |
| | | val existingCode = Core.code.queryByTypeAndCodeAndDate( |
| | | response.data.category, |
| | | response.data.details.amount ?: "", |
| | | amount, |
| | | dateString |
| | | ) |
| | | |
| | |
| | | createTime = dateString, |
| | | oneLevel = response.data.details.bank ?: "", |
| | | secondLevel = response.data.details.bank ?: "", |
| | | code = response.data.details.amount ?: "", |
| | | code = amount, |
| | | pickup = 0, // 0-未取件,1-已取件 |
| | | pickupTime = "", // 取件时间为空 |
| | | overTime = response.data.details.datetime |
| | |
| | | address = response.data.details.address ?: "", |
| | | remarks = "余额" + response.data.details.balance ?: "", |
| | | ) |
| | | if(code.oneLevel!="" && code.secondLevel!="" && code.code!="") { |
| | | if(code.oneLevel.isNotEmpty() && code.secondLevel.isNotEmpty() && code.code.isNotEmpty()) { |
| | | Core.code.insert(code) |
| | | android.util.Log.d("MainActivity", "历史还款短信已保存: $amount") |
| | | } |
| | | android.util.Log.d("MainActivity", "历史还款短信已保存: ${response.data.details.amount}") |
| | | } else { |
| | | android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: ${response.data.details.amount}") |
| | | android.util.Log.d("MainActivity", "发现重复还款短信,跳过保存: $amount") |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | private lateinit var toolbar: androidx.appcompat.widget.Toolbar |
| | | |
| | | companion object { |
| | | const val EXTRA_CONTENT_TYPE = "content_type" |
| | | const val ID = "id" |
| | | const val EXTRA_TITLE = "title" |
| | | } |
| | | |
| | |
| | | toolbar.setNavigationOnClickListener { finish() } |
| | | |
| | | // 获取内容类型 |
| | | val contentType = intent.getStringExtra(EXTRA_CONTENT_TYPE) |
| | | if (contentType != null) { |
| | | loadContent(contentType) |
| | | val id = intent.getStringExtra(ID) |
| | | if (id != null) { |
| | | loadContent(id) |
| | | } else { |
| | | Toast.makeText(this, "参数错误", Toast.LENGTH_SHORT).show() |
| | | finish() |
| | | } |
| | | } |
| | | |
| | | private fun loadContent(type: String) { |
| | | private fun loadContent(id: String) { |
| | | lifecycleScope.launch { |
| | | try { |
| | | val response = RetrofitClient.apiService.getContentByType(type) |
| | | if (response.code == 200 && response.data != null) { |
| | | val response = RetrofitClient.apiService.getContentById(id) |
| | | if (response.code == "0" && response.data != null) { |
| | | // 构建HTML内容 |
| | | val htmlContent = """ |
| | | <!DOCTYPE html> |
| | |
| | | private fun handleVipProtocolClick(){ |
| | | |
| | | binding.protocolVip.setOnClickListener{ |
| | | startContentActivity("privacy_policy", "VIP会员服务协议") |
| | | startContentActivity("VIP会员服务协议", "VIP会员服务协议") |
| | | } |
| | | |
| | | } |
| | | |
| | | private fun startContentActivity(type: String, title: String) { |
| | | val intent = Intent(this, ContentDetailActivity::class.java).apply { |
| | | putExtra(ContentDetailActivity.EXTRA_CONTENT_TYPE, type) |
| | | putExtra(ContentDetailActivity.ID, type) |
| | | putExtra(ContentDetailActivity.EXTRA_TITLE, title) |
| | | } |
| | | startActivity(intent) |
| | |
| | | """) |
| | | fun getByKeyword(keyword: String): List<Code> |
| | | |
| | | @Query("SELECT * FROM Code WHERE oneLevel = :content and code= :code and createTime = :dateString LIMIT 1") |
| | | fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code |
| | | @Query(""" |
| | | SELECT * FROM Code |
| | | WHERE category = :category |
| | | AND code = :code |
| | | AND substr(createTime, 1, 10) = substr(:dateString, 1, 10) |
| | | LIMIT 1 |
| | | """) |
| | | fun queryByTypeAndCodeAndDate(category: String, code: String, dateString: String): Code? |
| | | |
| | | |
| | | @Query("update Code set pickup = '1' , pickuptime = CURRENT_TIMESTAMP where id=:id") |
| | |
| | | return codeDao.getByKeyword(keyword) |
| | | } |
| | | |
| | | fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code { |
| | | @WorkerThread |
| | | fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code? { |
| | | return codeDao.queryByTypeAndCodeAndDate(content, code, dateString) |
| | | } |
| | | |
| | |
| | | package com.example.firstapp.database.response |
| | | |
| | | data class ContentResponse( |
| | | val code: Int, |
| | | val code: String, |
| | | val msg: String, |
| | | val data: ContentData? |
| | | ) |
| | | |
| | | data class ContentData( |
| | | val id: Long, |
| | | val type: String, |
| | | val id: String, |
| | | val title: String, |
| | | val content: String, |
| | | val version: String, |
| | | val status: Int, |
| | | val createTime: String, |
| | | val updateTime: String, |
| | | val creator: String?, |
| | | val updater: String? |
| | | ) |
| | |
| | | @GET("keywords") |
| | | suspend fun getKeywords():ApiResponse<List<KeywordConfig>> //异步挂起 |
| | | |
| | | @GET("cloudContent/getByType") |
| | | suspend fun getContentByType(@Query("type") type: String): ContentResponse |
| | | @GET("api/config/content/list/view") |
| | | suspend fun getContentById(@Query("id") id: String): ContentResponse |
| | | |
| | | @GET("sysDict/getByDictCodeAndItemText") |
| | | suspend fun getDictValue(@Query("dictCode") dictCode: String, @Query("itemText") itemText: String): DictResponse |
| | |
| | | // 创建Retrofit实例(单例) |
| | | object RetrofitClient{ |
| | | |
| | | private const val BASE_URL ="http://192.168.1.199:8080/flower/" |
| | | private const val BASE_URL ="http://192.168.1.213:8080/flower/" |
| | | |
| | | // 创建OkHttpClient,配置拦截器和超时时间 |
| | | private val okHttpClient = OkHttpClient.Builder() |
| | |
| | | import com.example.firstapp.R |
| | | import com.example.firstapp.activity.ContentDetailActivity |
| | | import com.example.firstapp.activity.PickupActivity |
| | | import com.example.firstapp.activity.VipActivity |
| | | import com.example.firstapp.adapter.ExpressAdapter |
| | | import com.example.firstapp.adapter.FinanceAdapter |
| | | import com.example.firstapp.adapter.CategorySelectorAdapter |
| | |
| | | import com.example.firstapp.database.service.RetrofitClient |
| | | import com.example.firstapp.databinding.FragmentHomeBinding |
| | | import com.example.firstapp.databinding.DialogCategorySelectorBinding |
| | | import com.example.firstapp.model.CategoryConfig |
| | | import com.example.firstapp.model.IncomeGroup |
| | | import com.example.firstapp.model.IncomePackage |
| | | import com.example.firstapp.utils.PreferencesManager |
| | |
| | | super.onViewCreated(view, savedInstanceState) |
| | | |
| | | homeViewModel = ViewModelProvider(this).get(HomeViewModel::class.java) |
| | | // 假设从某处获取用户ID |
| | | // val userId = getUserId() // 需要实现这个方法 |
| | | val userId ="123456" |
| | | homeViewModel.initialize(requireContext(), userId) |
| | | |
| | | // 设置点击监听事件 |
| | | // 检查是否是首次安装 |
| | | val isFirstInstall = PreferencesManager.isFirstInstall() |
| | | if (isFirstInstall) { |
| | | // 首次安装,设置默认显示快递和还款 |
| | | val defaultCategories = listOf( |
| | | CategoryConfig( |
| | | id = 1, |
| | | name = "快递", |
| | | order = 1, |
| | | isEnabled = true |
| | | ), |
| | | CategoryConfig( |
| | | id = 2, |
| | | name = "还款", |
| | | order = 2, |
| | | isEnabled = true |
| | | ) |
| | | ) |
| | | homeViewModel.saveCategories(defaultCategories) |
| | | // 标记为非首次安装 |
| | | PreferencesManager.setFirstInstall(false) |
| | | } |
| | | |
| | | setupAdapters() |
| | | setupTabSwitching() |
| | | setupObservers() |
| | |
| | | tabExpress.setTextColor(ContextCompat.getColor(requireContext(), R.color.tab_selected)) |
| | | tabFinance.setTextColor(ContextCompat.getColor(requireContext(), R.color.gray)) |
| | | |
| | | // 快递标签点击事件 - 快递功能所有用户都可以使用 |
| | | // 快递标签点击事件 |
| | | tabExpress.setOnClickListener { |
| | | hideAllRecyclers() |
| | | expressRecycler.visibility = View.VISIBLE |
| | |
| | | homeViewModel.loadExpressData() |
| | | } |
| | | |
| | | // 还款标签点击事件 - 非会员也可以使用 |
| | | tabFinance.setOnClickListener { |
| | | hideAllRecyclers() |
| | | financeRecycler.visibility = View.VISIBLE |
| | | updateTabStyles(tabFinance) |
| | | homeViewModel.loadFinanceData() |
| | | } |
| | | |
| | | // 其他标签点击事件需要检查会员状态 |
| | | val memberOnlyTabs = mapOf( |
| | | tabFinance to { homeViewModel.loadFinanceData() }, |
| | | tabIncome to { homeViewModel.loadIncomeData() }, |
| | | tabFlight to { homeViewModel.loadFlightData() }, |
| | | tabTrain to { homeViewModel.loadTrainData() } |
| | |
| | | checkMembershipAndExecute(tab) { |
| | | hideAllRecyclers() |
| | | when (tab) { |
| | | tabFinance -> financeRecycler.visibility = View.VISIBLE |
| | | tabIncome -> incomeRecycler.visibility = View.VISIBLE |
| | | tabFlight -> flightRecycler.visibility = View.VISIBLE |
| | | tabTrain -> trainRecycler.visibility = View.VISIBLE |
| | |
| | | } |
| | | |
| | | // 观察可见分类的变化 |
| | | homeViewModel.visibleCategories.observe(viewLifecycleOwner) { categories: List<String> -> |
| | | homeViewModel.visibleCategories.observe(viewLifecycleOwner) { categories: List<CategoryConfig> -> |
| | | binding.apply { |
| | | // 隐藏所有标签 |
| | | tabExpress.visibility = View.GONE |
| | |
| | | tabFlight.visibility = View.GONE |
| | | tabTrain.visibility = View.GONE |
| | | |
| | | // 根据选中的分类显示对应的标签 |
| | | categories.forEachIndexed { index: Int, categoryName: String -> |
| | | when (categoryName) { |
| | | // 非会员只显示快递和还款 |
| | | val savedPhone = PreferencesManager.getPhone() |
| | | lifecycleScope.launch { |
| | | try { |
| | | val response = RetrofitClient.apiService.getUserInfo(savedPhone ?: "") |
| | | val isMember = response.code == "0" && response.data?.isMember == true |
| | | |
| | | if (!isMember) { |
| | | // 非会员只显示快递和还款 |
| | | tabExpress.visibility = View.VISIBLE |
| | | tabFinance.visibility = View.VISIBLE |
| | | if (categories.firstOrNull()?.name == "快递") { |
| | | tabExpress.performClick() |
| | | } else { |
| | | tabFinance.performClick() |
| | | } |
| | | } else { |
| | | // 会员显示所有选中的分类 |
| | | categories.forEach { category -> |
| | | when (category.name) { |
| | | "快递" -> { |
| | | tabExpress.visibility = View.VISIBLE |
| | | if (index == 0) tabExpress.performClick() |
| | | if (categories.indexOf(category) == 0) tabExpress.performClick() |
| | | } |
| | | "还款" -> { |
| | | tabFinance.visibility = View.VISIBLE |
| | | if (index == 0) tabFinance.performClick() |
| | | if (categories.indexOf(category) == 0) tabFinance.performClick() |
| | | } |
| | | "收入" -> { |
| | | tabIncome.visibility = View.VISIBLE |
| | | if (index == 0) tabIncome.performClick() |
| | | if (categories.indexOf(category) == 0) tabIncome.performClick() |
| | | } |
| | | "航班" -> { |
| | | tabFlight.visibility = View.VISIBLE |
| | | if (index == 0) tabFlight.performClick() |
| | | if (categories.indexOf(category) == 0) tabFlight.performClick() |
| | | } |
| | | "火车票" -> { |
| | | tabTrain.visibility = View.VISIBLE |
| | | if (index == 0) tabTrain.performClick() |
| | | if (categories.indexOf(category) == 0) tabTrain.performClick() |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | // 发生错误时默认显示快递和还款 |
| | | tabExpress.visibility = View.VISIBLE |
| | | tabFinance.visibility = View.VISIBLE |
| | | tabExpress.performClick() |
| | | } |
| | | } |
| | | } |
| | |
| | | if (response.data.isMember) { |
| | | showCategorySelectorDialog() |
| | | } else { |
| | | Toast.makeText(requireContext(), "该功能仅对会员开放", Toast.LENGTH_SHORT).show() |
| | | // 非会员跳转到VIP开通页面 |
| | | val intent = Intent(requireContext(), VipActivity::class.java) |
| | | startActivity(intent) |
| | | } |
| | | } else { |
| | | Toast.makeText(requireContext(), "获取用户信息失败", Toast.LENGTH_SHORT).show() |
| | |
| | | private val _categories = MutableLiveData<List<CategoryConfig>>() |
| | | val categories: LiveData<List<CategoryConfig>> = _categories |
| | | |
| | | // 添加可见分类的 LiveData |
| | | private val _visibleCategories = MutableLiveData<List<String>>() |
| | | val visibleCategories: LiveData<List<String>> = _visibleCategories |
| | | private val _visibleCategories = MutableLiveData<List<CategoryConfig>>() |
| | | val visibleCategories: LiveData<List<CategoryConfig>> = _visibleCategories |
| | | |
| | | private lateinit var secureStorage: SecureStorage |
| | | private lateinit var currentUserId: String |
| | |
| | | } |
| | | |
| | | fun saveCategories(categories: List<CategoryConfig>) { |
| | | viewModelScope.launch { |
| | | // 保存到本地 |
| | | secureStorage.saveCategories(currentUserId, categories) |
| | | // 同步到服务器 |
| | | syncCategoriesToServer(categories) |
| | | _categories.value = categories |
| | | |
| | | // 更新可见分类 |
| | | updateVisibleCategories(categories) |
| | | } |
| | | _visibleCategories.value = categories.filter { it.isEnabled } |
| | | } |
| | | |
| | | private fun updateVisibleCategories(categories: List<CategoryConfig>) { |
| | |
| | | .sortedBy { it.order } |
| | | .map { it.name } |
| | | |
| | | _visibleCategories.value = visibleNames |
| | | _visibleCategories.value = categories.filter { it.isEnabled } |
| | | } |
| | | |
| | | // 登出时不再清除本地数据 |
| | |
| | | |
| | | // 隐私协议 |
| | | binding.layoutPrivacy.setOnClickListener { |
| | | startContentActivity("privacy_policy", "隐私协议") |
| | | startContentActivity("隐私协议", "隐私协议") |
| | | } |
| | | |
| | | // 使用教程 |
| | | binding.layoutTutorial.setOnClickListener { |
| | | startContentActivity("user_guide", "使用教程") |
| | | startContentActivity("使用教程", "使用教程") |
| | | } |
| | | |
| | | // 头像点击老的处理逻辑 |
| | |
| | | } |
| | | } |
| | | |
| | | private fun startContentActivity(type: String, title: String) { |
| | | private fun startContentActivity(id: String, title: String) { |
| | | val intent = Intent(requireContext(), ContentDetailActivity::class.java).apply { |
| | | putExtra(ContentDetailActivity.EXTRA_CONTENT_TYPE, type) |
| | | putExtra(ContentDetailActivity.ID, id) |
| | | putExtra(ContentDetailActivity.EXTRA_TITLE, title) |
| | | } |
| | | startActivity(intent) |
| | |
| | | private const val PREF_NAME = "app_preferences" |
| | | private const val KEY_TOKEN = "user_token" |
| | | private const val KEY_PHONE = "user_phone" |
| | | private const val KEY_FIRST_INSTALL = "first_install" |
| | | |
| | | private lateinit var preferences: SharedPreferences |
| | | |
| | |
| | | apply() |
| | | } |
| | | } |
| | | |
| | | fun isFirstInstall(): Boolean { |
| | | return preferences.getBoolean(KEY_FIRST_INSTALL, true) |
| | | } |
| | | |
| | | fun setFirstInstall(isFirst: Boolean) { |
| | | preferences.edit().putBoolean(KEY_FIRST_INSTALL, isFirst).apply() |
| | | } |
| | | } |
| | |
| | | android:layout_width="match_parent" |
| | | android:layout_height="40dp" |
| | | android:scaleType="centerCrop" |
| | | android:src="@drawable/up" /> |
| | | android:src="@drawable/up_back" /> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <!-- 在适当的位置添加 --> |