iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Software Development

從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題系列 第 16 篇

Day 16 | 從 CSV 到畫面:RoomRush 的資料流我終於串起來了

  • 分享至 

  • xImage
  •  

前言:CSV 到 ViewModel 再到畫面的完整資料流是什麼?

在第二階段「資料來源主線」,我們在前面介紹到以下這幾個檔案:

  • AppDatabase.kt:建立 Room Database,讀 CSV 並寫入資料庫。
  • ClassroomSchedule.kt:Room Entity,一筆資料代表一間教室的一週課表。
  • ClassroomScheduleDao.kt:定義資料庫操作。
  • ClassroomScheduleRepository.kt:包裝 DAO,提供資料入口給 ViewModel。
  • AppContainer.kt:建立 Database / DAO / Repository。
  • EmptyRoomFinderApp.kt:建立 AppContainer,讓 Activity 可以取得共用依賴。

檔案順序雖然和前幾天文章稍微有點不一樣,但這是方便我們看懂整個資料流程的順序。
透過今天的檔案,希望不論是自己,還是讀者,都能了解 RoomRush 的教室課表資料是如何從 SF.csv/ES.csv 進到 Room Database,再透過 DAO、Repository 提供給 ViewModel 使用。


資料來源主線總結

1. 初始資料源:大樓原始課表

RoomRush 的初始課表資料是在 SF.csv 以及 ES.csv ,分別對應到聖言樓教室以及進修部大樓教室,兩者分別存放不同的教室課表。

2. 資料庫翻譯:AppDatabase.kt 把 CSV 轉成資料庫資料

AppDatabase 建立 Room Database,提供 ClassroomScheduleDao,讀取 SF.csv / ES.csv,用 parseCsv() 依固定欄位順序建立 ClassroomSchedule,合併後用 insertAll() 寫入資料庫。

3. 資料庫結構模型:ClassroomSchedule.kt (Entity)

ClassroomSchedule 是 RoomRush 的 Room Entity,對應 classroom_schedules 資料表;每一筆代表一間教室的一週課表;classroom 是主鍵,用來唯一識別教室。

4. 資料庫操作介面:ClassroomScheduleDao.kt (DAO)

ClassroomScheduleDao 是資料存取物件,負責定義如何操作 classroom_schedules 資料表;包含: getAll() 用來取得所有課表資料、 insert 單筆新增且衝突時忽略;insertAll 批次新增且衝突時覆蓋;update 更新既有資料;delete 刪除資料、getSchedulesByBuildingAndFloor()、getClassroomDetails()等等。

5. 資料中介樞紐:ClassroomScheduleRepository.kt (Repository)

ClassroomScheduleRepository 是負責 ViewModel 和 DAO 中間的資料層中介,提供 getAllSchedules()、insert()、insertAll()、update()、delete()、findByName()、getClassroomDetails() 等在 DAO 用到的方法給 ViewModel 使用。

6. 依賴注入中心:EmptyRoomFinderApp → AppContainer

EmptyRoomFinderApp 建立 AppContainer → AppContainer 用 Context 取得 Database → 從 Database 取得 DAO → 用 DAO 建立 Repository → Activity 從 Container 取得 Repository;Activity 通常會把 Repository 傳給 ViewModelFactory,再建立 ViewModel。

7. 最終運算產出:QueryResultViewModel 產出 emptyRooms

QueryResultViewModel 是透過 repository.getAllSchedules() 取得所有課表。

再通過三個篩選條件才會被放進 emptyRooms 供畫面顯示:

  1. 大樓(building)符合(如選取聖言樓 ➔ 教室開頭為 SF)
  2. 樓層(floor)符合(如選取 3 樓 ➔ 教室編號包含 3)
  3. 指定時段(day / time)可用(精準判定條件為該節次欄位代碼 != "X")

這條資料庫主線所遇到的技術債

  1. 查詢邏輯重複與分散:

    DAO 和 Repository 裡原本就寫了 queryEmptyRooms 的篩選邏輯,但目前主要查詢頁的空教室篩選邏輯是在 QueryResultViewModel ,造成邏輯分散兩處。

  2. 每次開啟的資料覆蓋風險:

    DatabaseCallback 的 onOpen 每次都會觸發讀取 CSV 並執行 insertAll(REPLACE);代表每次資料庫打開時可能重新讀 CSV 並 insertAll,管理功能修改過資料可能被 CSV 覆蓋;如果未來 App 允許使用者手動修改或自訂教室資訊,這些自訂資料在 App 重開時會直接被 CSV 暴力覆蓋還原。

  3. CSV 欄位順序依賴:

    parseCsv() 內部是硬編碼(Hardcoded)依照索引順序(parts[0], parts[1]...)來讀取 47 個欄位,只要 CSV 欄位順序稍有微調,整個解析可能就會錯位。

  4. catch 吞掉錯誤:

    CSV 解析過程中的 try-catch 在發生例外時直接回傳 emptyList,雖然防止了 Crash,卻也讓開發者在發生錯誤時難以發現哪裡有錯。


完整流程圖

SF.csv / ES.csv
    ↓
assets 中的初始課表資料
    ↓
AppDatabase
    ↓
讀取 CSV 並解析資料
    ↓
ClassroomSchedule
    ↓
一筆資料代表一間教室的一週課表
    ↓
ClassroomScheduleDao
    ↓
執行資料庫操作
    ↓
Room Database
    ↓
儲存 ClassroomSchedule 資料
    ↓
ClassroomScheduleRepository
    ↓
統整資料存取
    ↓
AppContainer
    ↓
建立並提供 Database、DAO、Repository
    ↓
EmptyRoomFinderApp
    ↓
提供 AppContainer
    ↓
Activity / ViewModelFactory
    ↓
取得 Repository 並傳給 ViewModel
    ↓
QueryResultViewModel
    ↓
根據大樓、樓層、星期、時段篩選資料
    ↓
emptyRooms
    ↓
得到符合條件的空教室

Hermes Agent 幫我檢查出的重點

RoomRush 的初始課表資料存放在 assets 裡的 SF.csv 和 ES.csv,分別對應聖言樓與進修部大樓。

  • AppDatabase 在資料庫建立或開啟時讀取 CSV,透過 parseCsv() 略過表頭、依固定欄位順序解析每一列,建立 ClassroomSchedule 物件,最後用 dao.insertAll() 寫入 Room Database。
  • ClassroomSchedule 是 Entity,一筆代表一間教室的一週課表。
  • ClassroomScheduleDao 定義資料表操作;
  • ClassroomScheduleRepository 包裝 DAO 給 ViewModel 使用。
  • EmptyRoomFinderApp 建立 AppContainer;
  • AppContainer 用 Context 取得 Database、DAO 並建立 Repository。
  • 最後 QueryResultViewModel 透過 repository.getAllSchedules() 取得所有課表,根據大樓、樓層、星期與時段篩出 emptyRooms。

流程分工表

階段 檔案 / 資料 負責什麼 產生 / 傳遞什麼
1 SF.csv / ES.csv 初始課表資料 CSV row
2 AppDatabase.kt 建立資料庫、讀取 CSV、預載資料 ClassroomSchedule list
3 ClassroomSchedule.kt 定義 Entity / 資料表形狀 classroom_schedules row
4 ClassroomScheduleDao.kt 定義資料庫操作 Flow / CRUD / 查詢方法
5 ClassroomScheduleRepository.kt 包裝 DAO,提供給 ViewModel 使用 Repository API
6 AppContainer.kt 建立 Database / DAO / Repository classroomScheduleRepository
7 EmptyRoomFinderApp.kt 建立並保存 AppContainer appContainer
8 QueryResultViewModel.kt 使用 Repository 資料篩選空教室 emptyRooms

小結

這次整理資料來源主線後,我終於看懂 RoomRush 的空教室資料是怎麼來的。

它不是直接寫在 ViewModel 裡,也不是使用者臨時輸入,而是從 assets 裡的 SF.csv 和 ES.csv 開始。
AppDatabase 會讀取這兩份 CSV,把每一列轉成 ClassroomSchedule,再寫進 Room Database。
接著,DAO 負責定義資料表操作,Repository 則把 DAO 包裝成 ViewModel 可以使用的方法。
最後,EmptyRoomFinderApp 和 AppContainer 負責建立並提供 Repository,讓 QueryResultViewModel 可以取得所有課表資料並篩選出空教室。

這條資料流也讓我看到幾個技術債:CSV 匯入可能覆蓋資料庫修改、CSV 欄位順序高度依賴程式碼、錯誤處理會把例外吞掉,以及空教室查詢邏輯分散在 Repository 和 ViewModel。

這些問題不一定影響目前展示,但如果要把專案整理得更穩定,就需要被記錄與改善。


下一篇預告:我發現一個危險問題:管理者修改可能被 CSV 蓋掉

今天,我們已經把這條資料來源主線拆開看過:知道 CSV 如何被解析成 ClassroomSchedule、DAO 如何操作資料、Repository 如何提供給 ViewModel,以及最後如何被查詢頁使用。

但在接續到了管理者功能前,我想反過來看另一件事:如果這些資料不是只有被查詢,而是還會被管理者修改呢?

因此下一篇,我們會先來探究 AppDatabase.kt 提到的:onOpen 每次開啟資料庫都會預載 CSV,如果管理者曾修改資料,可能被 CSV 覆蓋。


上一篇
Day 15 | Repository 從哪裡生出來?我第一次看懂 AppContainer 的用途
下一篇
Day 17 | 我發現一個危險問題:管理者修改可能被 CSV 蓋掉
系列文
從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言