|  |  | 
 |  |  |                                 android.util.Log.d("SmsReceiver", "Received SMS code: ${code}") | 
 |  |  |                                 val msg = Msg(0, "1111", "111111", messageBody.toString(), 1, "111", 1, 1) | 
 |  |  |                                 val msgId = Core.msg.insert(msg) | 
 |  |  |                                 val code = Code(0, rule.type, 1, rule.content, 1, 1, msgId, code, dateString, "中通",0) | 
 |  |  |                                 val code = Code(0, rule.type, 1, rule.content, 1, 1, msgId, code, dateString, "中通",0,"","") | 
 |  |  |                                 Core.code.insert(code) | 
 |  |  |                                 android.util.Log.d("SMS_DEBUG", "历史短信已保存到数据库") | 
 |  |  |                             }else{ | 
 
 |  |  | 
 |  |  | import android.widget.TextView | 
 |  |  | import androidx.recyclerview.widget.RecyclerView | 
 |  |  | import com.example.firstapp.R | 
 |  |  | import com.example.firstapp.model.PackageInfo | 
 |  |  | import com.example.firstapp.database.entity.Code | 
 |  |  | import java.text.ParseException | 
 |  |  | import java.text.SimpleDateFormat | 
 |  |  | import java.util.Date | 
 |  |  | import java.util.Locale | 
 |  |  |  | 
 |  |  |  | 
 |  |  | class PackageAdapter : RecyclerView.Adapter<PackageAdapter.PackageViewHolder>() { | 
 |  |  |  | 
 |  |  |     private var packages = listOf<PackageInfo>() | 
 |  |  |     private var packages = listOf<Code>() | 
 |  |  |  | 
 |  |  |     fun updatePackages(newPackages: List<PackageInfo>) { | 
 |  |  |     fun updatePackages(newPackages: List<Code>) { | 
 |  |  |         packages = newPackages | 
 |  |  |         notifyDataSetChanged() | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     //此处是渲染数据 | 
 |  |  |     override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PackageViewHolder { | 
 |  |  |         val view = LayoutInflater.from(parent.context) | 
 |  |  |             .inflate(R.layout.item_package_dashboard, parent, false) | 
 |  |  | 
 |  |  |         private val textCourierName: TextView = view.findViewById(R.id.text_courier_name) | 
 |  |  |         private val textTrackingNumber: TextView = view.findViewById(R.id.text_tracking_number) | 
 |  |  |         private val textTime: TextView = view.findViewById(R.id.text_time) | 
 |  |  |         private val textPickTime: TextView = view.findViewById(R.id.text_pick_time) | 
 |  |  |  | 
 |  |  |         fun bind(packageInfo: PackageInfo) { | 
 |  |  |             imgCourier.setImageResource(packageInfo.courierIcon) | 
 |  |  |             textCourierName.text = packageInfo.courierName | 
 |  |  |             textTrackingNumber.text = packageInfo.trackingNumber | 
 |  |  |             textTime.text = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()) | 
 |  |  |                 .format(packageInfo.receivedTime) | 
 |  |  |         fun bind(code: Code) { | 
 |  |  | //            imgCourier.setImageResource(code.category) | 
 |  |  |             textCourierName.text = code.type | 
 |  |  |             textTrackingNumber.text = code.code | 
 |  |  |             // 步骤1:定义解析器,将字符串转为 Date | 
 |  |  |             val parser = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()) | 
 |  |  |             val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault()) | 
 |  |  |             try { | 
 |  |  |                 val date: Date? = parser.parse(code.createtime)  // 解析字符串 | 
 |  |  |                 date?.let { | 
 |  |  |                     textTime.text = "到货:"+formatter.format(it)  // 格式化并赋值 | 
 |  |  |                 } ?: run { | 
 |  |  |                     // 处理解析失败(date 为 null 的情况) | 
 |  |  |                     textTime.text = "Invalid Date" | 
 |  |  |                 } | 
 |  |  |             } catch (e: ParseException) { | 
 |  |  |                 e.printStackTrace() | 
 |  |  |                 textTime.text = "Format Error" | 
 |  |  |             } | 
 |  |  |             try { | 
 |  |  |                 val date2: Date? = parser.parse(code.pickuptime)  // 解析字符串 | 
 |  |  |                 date2?.let { | 
 |  |  |                     textPickTime.text = "取件:"+formatter.format(it)  // 格式化并赋值 | 
 |  |  |                 } ?: run { | 
 |  |  |                     textPickTime.text = "未取件" | 
 |  |  |                 } | 
 |  |  |             } catch (e: ParseException) { | 
 |  |  |                 e.printStackTrace() | 
 |  |  |                 textPickTime.text = "未取件" | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  | } | 
 
 |  |  | 
 |  |  | import androidx.room.TypeConverters | 
 |  |  | import androidx.room.migration.Migration | 
 |  |  | import androidx.sqlite.db.SupportSQLiteDatabase | 
 |  |  | import com.example.firstapp.dao.PackageDao | 
 |  |  | import com.example.firstapp.database.dao.CodeDao | 
 |  |  | import com.example.firstapp.database.dao.KeywordDao | 
 |  |  | import com.example.firstapp.database.dao.MsgDao | 
 |  |  | 
 |  |  | import com.example.firstapp.utils.TAG_LIST | 
 |  |  |  | 
 |  |  | import com.example.firstapp.database.ext.ConvertersDate | 
 |  |  | import com.example.firstapp.model.PackageInfo | 
 |  |  | import com.example.firstapp.model.CourierStat | 
 |  |  | import com.example.firstapp.model.DailyStat | 
 |  |  |  | 
 |  |  | 
 |  |  |         Msg::class,  | 
 |  |  |         Code::class,  | 
 |  |  |         KeywordEntity::class,  | 
 |  |  |         Reminder::class,  | 
 |  |  |         PackageInfo::class | 
 |  |  |         Reminder::class | 
 |  |  |     ], | 
 |  |  |     views = [ | 
 |  |  |         CourierStat::class, | 
 |  |  | 
 |  |  |     abstract fun codeDao(): CodeDao | 
 |  |  |     abstract fun keywordDao(): KeywordDao | 
 |  |  |     abstract fun reminderDao(): ReminderDao | 
 |  |  |     abstract fun packageDao(): PackageDao | 
 |  |  |  | 
 |  |  |     companion object { | 
 |  |  |         @Volatile | 
 
 |  |  | 
 |  |  | import androidx.room.Update | 
 |  |  | import com.example.firstapp.database.entity.Code | 
 |  |  | import com.example.firstapp.database.entity.Msg | 
 |  |  | import com.example.firstapp.model.CourierStat | 
 |  |  | import com.example.firstapp.model.DailyStat | 
 |  |  | import io.reactivex.Completable | 
 |  |  | import kotlinx.coroutines.flow.Flow | 
 |  |  |  | 
 |  |  | @Dao | 
 |  |  | interface CodeDao { | 
 |  |  | 
 |  |  |     fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     @Query("update  Code set pickup = '1' where id=:id") | 
 |  |  |     @Query("update  Code set pickup = '1' ,  pickuptime = CURRENT_TIMESTAMP  where id=:id") | 
 |  |  |     fun pickup(id: Long) | 
 |  |  |  | 
 |  |  |     //查询当天包裹信息 | 
 |  |  |     @Query("SELECT * FROM code WHERE date(createtime) = date(:date/1000, 'unixepoch', 'localtime') ORDER BY createtime DESC") | 
 |  |  |     fun getNewPackagesByDay(date: Long): List<Code> | 
 |  |  |  | 
 |  |  |     @Query("SELECT * FROM code WHERE date(createtime/1000, 'unixepoch', 'localtime') = date(:date/1000, 'unixepoch', 'localtime') ORDER BY createtime DESC") | 
 |  |  |     fun getPackagesByDay(date: Long): Flow<List<Code>> | 
 |  |  |  | 
 |  |  |     @Query(""" | 
 |  |  |         SELECT * FROM code  | 
 |  |  |         WHERE strftime('%Y-%W', createtime/1000, 'unixepoch', 'localtime') =  | 
 |  |  |               strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime')  | 
 |  |  |         ORDER BY createtime DESC | 
 |  |  |     """) | 
 |  |  |     fun getPackagesByWeek(date: Long): Flow<List<Code>> | 
 |  |  |  | 
 |  |  |     @Query(""" | 
 |  |  |         SELECT * FROM CourierStat  | 
 |  |  |         WHERE EXISTS ( | 
 |  |  |             SELECT 1 FROM code p  | 
 |  |  |             WHERE p.category = CourierStat.category | 
 |  |  |             AND strftime('%Y-%W', p.createtime/1000, 'unixepoch', 'localtime') =  | 
 |  |  |                 strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime') | 
 |  |  |         ) | 
 |  |  |     """) | 
 |  |  |     fun getCourierStatsByWeek(date: Long): Flow<List<CourierStat>> | 
 |  |  |  | 
 |  |  |     @Query(""" | 
 |  |  |         SELECT * FROM DailyStat  | 
 |  |  |         WHERE EXISTS ( | 
 |  |  |             SELECT 1 FROM code p  | 
 |  |  |             WHERE date(p.createtime/1000, 'unixepoch', 'localtime') = DailyStat.date | 
 |  |  |             AND strftime('%Y-%W', p.createtime/1000, 'unixepoch', 'localtime') =  | 
 |  |  |                 strftime('%Y-%W', :date/1000, 'unixepoch', 'localtime') | 
 |  |  |         ) | 
 |  |  |     """) | 
 |  |  |     fun getDailyStatsByWeek(date: Long): Flow<List<DailyStat>> | 
 |  |  | } | 
 
 |  |  | 
 |  |  |     var createtime: String, | 
 |  |  |     var name: String, | 
 |  |  |     var pickup: Int, | 
 |  |  |     var pickuptime: String,  //取件时间 | 
 |  |  |     var overtime: String,   // 超期时间 | 
 |  |  |     var time: Date = Date(), | 
 |  |  | //    var overtime: String, | 
 |  |  |  | 
 |  |  | ) | 
 
 |  |  | 
 |  |  | import androidx.annotation.WorkerThread | 
 |  |  | import com.example.firstapp.database.dao.CodeDao | 
 |  |  | import com.example.firstapp.database.entity.Code | 
 |  |  | import kotlinx.coroutines.Dispatchers | 
 |  |  | import kotlinx.coroutines.flow.Flow | 
 |  |  | import kotlinx.coroutines.withContext | 
 |  |  |  | 
 |  |  |  | 
 |  |  | class CodeRepository(private val codeDao: CodeDao) { | 
 |  |  | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     fun queryByTypeAndCodeAndDate(content: String, code: String, dateString: String): Code { | 
 |  |  |         return codeDao.queryByTypeAndCodeAndDate(content,code,dateString) | 
 |  |  |         return codeDao.queryByTypeAndCodeAndDate(content, code, dateString) | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     @WorkerThread | 
 |  |  |     fun pickup(id: Long) = codeDao.pickup(id) | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     fun getPackages(date: Long, dateType: String): Flow<List<Code>> { | 
 |  |  |         return when (dateType) { | 
 |  |  |             "DAY" -> codeDao.getPackagesByDay(date) | 
 |  |  |             "WEEK" -> codeDao.getPackagesByWeek(date) | 
 |  |  |             else -> codeDao.getPackagesByDay(date) | 
 |  |  |         } | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     fun getCourierStats(date: Long) = codeDao.getCourierStatsByWeek(date) | 
 |  |  |  | 
 |  |  |     fun getDailyStats(date: Long) = codeDao.getDailyStatsByWeek(date) | 
 |  |  |  | 
 |  |  |  | 
 |  |  |     @WorkerThread | 
 |  |  |     fun getPackagesByDay(date: Long): List<Code> { | 
 |  |  |         return codeDao.getNewPackagesByDay(date) | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |  | 
 |  |  | } | 
 
 |  |  | 
 |  |  |  | 
 |  |  | @DatabaseView( | 
 |  |  |     """ | 
 |  |  |     SELECT courierName, COUNT(*) as count  | 
 |  |  |     FROM packages  | 
 |  |  |     GROUP BY courierName | 
 |  |  |     SELECT category, COUNT(*) as count  | 
 |  |  |     FROM Code  | 
 |  |  |     GROUP BY category | 
 |  |  |     """ | 
 |  |  | ) | 
 |  |  | data class CourierStat( | 
 |  |  |     val courierName: String, | 
 |  |  |     val category: String, | 
 |  |  |     val count: Int | 
 |  |  | )  | 
 
 |  |  | 
 |  |  |  | 
 |  |  | @DatabaseView( | 
 |  |  |     """ | 
 |  |  |     SELECT date(receivedTime/1000, 'unixepoch', 'localtime') as date,  | 
 |  |  |     SELECT date(createtime/1000, 'unixepoch', 'localtime') as date,  | 
 |  |  |            COUNT(*) as count  | 
 |  |  |     FROM packages  | 
 |  |  |     GROUP BY date(receivedTime/1000, 'unixepoch', 'localtime') | 
 |  |  |     FROM code  | 
 |  |  |     GROUP BY date(createtime/1000, 'unixepoch', 'localtime') | 
 |  |  |     """ | 
 |  |  | ) | 
 |  |  | data class DailyStat( | 
 
 |  |  | 
 |  |  |                             val createtime = sdf.format(date) | 
 |  |  |                             val existingCode = Core.code.queryByTypeAndCodeAndDate(rule.content, code, createtime) | 
 |  |  |                             if (existingCode == null) { | 
 |  |  |                                 val code = Code(0, rule.type, 1, rule.content, 1, 1, msgId, code, createtime, "中通",0) | 
 |  |  |                                 val code = Code(0, rule.type, 1, rule.content, 1, 1, msgId, code, createtime, "中通",0,"","") | 
 |  |  |                                 Core.code.insert(code) | 
 |  |  |                                 Log.d("SMS_DEBUG", "新短信已保存到数据库") | 
 |  |  |  | 
 
 |  |  | 
 |  |  | import com.google.android.material.tabs.TabLayout | 
 |  |  | import androidx.recyclerview.widget.LinearLayoutManager | 
 |  |  | import com.example.firstapp.adapter.PackageAdapter | 
 |  |  | import com.example.firstapp.model.PackageInfo | 
 |  |  | import com.example.firstapp.core.Core | 
 |  |  | import com.github.mikephil.charting.charts.BarChart | 
 |  |  | import com.github.mikephil.charting.charts.PieChart | 
 |  |  | import com.github.mikephil.charting.components.Legend | 
 |  |  | 
 |  |  |     private fun updatePieChartData() { | 
 |  |  |         viewModel.getCourierStats(currentDate.timeInMillis).observe(viewLifecycleOwner) { stats -> | 
 |  |  |             val entries = stats.map { stat -> | 
 |  |  |                 PieEntry(stat.count.toFloat(), stat.courierName) | 
 |  |  |                 PieEntry(stat.count.toFloat(), stat.category) | 
 |  |  |             } | 
 |  |  |  | 
 |  |  |             val dataSet = PieDataSet(entries, "快递公司分布") | 
 |  |  | 
 |  |  |  | 
 |  |  |     private fun loadPackages() { | 
 |  |  |         // 这里应该从数据库或网络加载数据 | 
 |  |  |         // 这里使用模拟数据作为示例 | 
 |  |  |         val mockPackages = listOf( | 
 |  |  |             PackageInfo( | 
 |  |  |                 trackingNumber = "14-6-7023", | 
 |  |  |                 courierName = "某快递", | 
 |  |  |                 receivedTime = System.currentTimeMillis(), | 
 |  |  |                 courierIcon = R.drawable.data | 
 |  |  |             ), | 
 |  |  |             PackageInfo( | 
 |  |  |                 trackingNumber = "230721", | 
 |  |  |                 courierName = "京东", | 
 |  |  |                 receivedTime = System.currentTimeMillis() - 3600000, | 
 |  |  |                 courierIcon = R.drawable.data | 
 |  |  |             ) | 
 |  |  |         ) | 
 |  |  |  | 
 |  |  |         packageAdapter.updatePackages(mockPackages) | 
 |  |  |         binding.textPackageCount.text = "${mockPackages.size}个" | 
 |  |  |         // 根据当前选择的日期类型传入对应参数 | 
 |  |  |         val packages = when (currentDateType) { | 
 |  |  |             DateType.DAY -> Core.code.getPackagesByDay(currentDate.timeInMillis) | 
 |  |  | //            DateType.WEEK -> Core.code.getPackagesByWeek(currentDate.timeInMillis) | 
 |  |  | //            DateType.MONTH -> Core.code.getPackagesByMonth(currentDate.timeInMillis) | 
 |  |  | //            DateType.YEAR -> Core.code.getPackagesByYear(currentDate.timeInMillis) | 
 |  |  |             DateType.WEEK -> TODO() | 
 |  |  |             DateType.MONTH -> TODO() | 
 |  |  |             DateType.YEAR -> TODO() | 
 |  |  |         } | 
 |  |  |         packageAdapter.updatePackages(packages) | 
 |  |  |         binding.textPackageCount.text = "${packageAdapter.itemCount}个" | 
 |  |  |     } | 
 |  |  |     private fun observePackages() { | 
 |  |  |         viewModel.getPackages( | 
 |  |  | 
 |  |  |                     updatePieChartData() | 
 |  |  |                 } | 
 |  |  |                 else -> { | 
 |  |  |                     packageAdapter.updatePackages(packages) | 
 |  |  |                 } | 
 |  |  |                 packageAdapter.updatePackages(packages) | 
 |  |  |             } | 
 |  |  |             } | 
 |  |  |             binding.textPackageCount.text = "${packages.size}个" | 
 |  |  |         } | 
 
 |  |  | 
 |  |  | import androidx.lifecycle.asLiveData | 
 |  |  | import androidx.lifecycle.viewModelScope | 
 |  |  | import com.example.firstapp.AppDatabase | 
 |  |  | import com.example.firstapp.model.PackageInfo | 
 |  |  | import com.example.firstapp.repository.PackageRepository | 
 |  |  | import com.example.firstapp.database.entity.Code | 
 |  |  | import com.example.firstapp.database.repository.CodeRepository | 
 |  |  | import kotlinx.coroutines.launch | 
 |  |  |  | 
 |  |  | class DashboardViewModel(application: Application) : AndroidViewModel(application) { | 
 |  |  |      | 
 |  |  |     private val repository: PackageRepository | 
 |  |  |      | 
 |  |  |  | 
 |  |  |     private val repository: CodeRepository | 
 |  |  |  | 
 |  |  |     init { | 
 |  |  |         val packageDao = AppDatabase.getInstance(application).packageDao() | 
 |  |  |         repository = PackageRepository(packageDao) | 
 |  |  |         val codeDao = AppDatabase.getInstance(application).codeDao() | 
 |  |  |         repository = CodeRepository(codeDao) | 
 |  |  |     } | 
 |  |  |      | 
 |  |  |     fun getPackages(date: Long, dateType: String) =  | 
 |  |  |  | 
 |  |  |     fun getPackages(date: Long, dateType: String) = | 
 |  |  |         repository.getPackages(date, dateType).asLiveData() | 
 |  |  |  | 
 |  |  |     fun getCourierStats(date: Long) = repository.getCourierStats(date).asLiveData() | 
 |  |  |      | 
 |  |  |  | 
 |  |  |     fun getDailyStats(date: Long) = repository.getDailyStats(date).asLiveData() | 
 |  |  |  | 
 |  |  |     fun updatePackageStatus(packageInfo: PackageInfo) = viewModelScope.launch { | 
 |  |  |         repository.update(packageInfo) | 
 |  |  |     } | 
 |  |  |  | 
 |  |  |     fun insert(packageInfo: PackageInfo) = viewModelScope.launch { | 
 |  |  |         repository.insert(packageInfo) | 
 |  |  |     fun insert(code: Code) = viewModelScope.launch { | 
 |  |  |         repository.insert(code) | 
 |  |  |     } | 
 |  |  | } | 
 
 |  |  | 
 |  |  |             android:padding="16dp"> | 
 |  |  |  | 
 |  |  |             <ImageView | 
 |  |  |                 android:layout_width="48dp" | 
 |  |  |                 android:layout_width="0dp" | 
 |  |  |                 android:layout_height="48dp" | 
 |  |  |                 android:layout_weight="1" | 
 |  |  |                 android:src="@drawable/resource_package" /> | 
 |  |  |  | 
 |  |  |             <TextView | 
 |  |  |                 android:id="@+id/text_package_count" | 
 |  |  |                 android:layout_width="wrap_content" | 
 |  |  |                 android:layout_width="0dp" | 
 |  |  |                 android:layout_height="wrap_content" | 
 |  |  |                 android:layout_gravity="center_vertical" | 
 |  |  |                 android:layout_marginStart="16dp" | 
 |  |  |                 android:layout_weight="1" | 
 |  |  |                 android:textSize="24sp" | 
 |  |  |                 android:textStyle="bold" | 
 |  |  |                 tools:text="4个" /> | 
 
 |  |  | 
 |  |  |                 tools:text="2025-01-14 10:30" /> | 
 |  |  |         </LinearLayout> | 
 |  |  |  | 
 |  |  |         <TextView | 
 |  |  |             android:id="@+id/text_tracking_number" | 
 |  |  |         <LinearLayout | 
 |  |  |             android:layout_width="wrap_content" | 
 |  |  |             android:layout_height="wrap_content" | 
 |  |  |             android:textColor="@android:color/darker_gray" | 
 |  |  |             tools:text="14-6-7023" /> | 
 |  |  |             android:orientation="vertical"> | 
 |  |  |  | 
 |  |  |             <TextView | 
 |  |  |                 android:id="@+id/text_tracking_number" | 
 |  |  |                 android:layout_width="wrap_content" | 
 |  |  |                 android:layout_height="wrap_content" | 
 |  |  |                 android:textColor="@android:color/darker_gray" | 
 |  |  |                 android:layout_gravity="end" | 
 |  |  |                 tools:text="14-6-7023" /> | 
 |  |  |  | 
 |  |  |             <TextView | 
 |  |  |                 android:id="@+id/text_pick_time" | 
 |  |  |                 android:layout_width="wrap_content" | 
 |  |  |                 android:layout_height="wrap_content" | 
 |  |  |                 android:layout_marginTop="4dp" | 
 |  |  |                 android:textColor="@android:color/darker_gray" | 
 |  |  |                 android:layout_gravity="end" | 
 |  |  |                 android:textSize="12sp" | 
 |  |  |                 tools:text="2025-01-15 11:30" /> | 
 |  |  |         </LinearLayout> | 
 |  |  |  | 
 |  |  |     </LinearLayout> | 
 |  |  |  | 
 |  |  | </androidx.cardview.widget.CardView>  |