iT邦幫忙

2026 iThome 鐵人賽

DAY 23
0
Software Development

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

Day 23 | 如果我是管理者,我怎麼一次看懂所有教室狀態?

  • 分享至 

  • xImage
  •  

前言:管理者如何看所有教室、如何新增教室?

前一天我們完成了第二條管理者主線的「教室細項設定」後,接下來我們會進入到第三條管理者功能-「所有教室狀態」。

今天我們會說明這條功能線,但我們會先著重在 AllClassroomsActivity.kt,也就是所有教室狀態的總覽頁。而和前一條主線一樣,這頁面也會有 Adapter 以及接收 CLASSROOM_NAME的檔案來做職責分離,這會在我們後面幾天的文章說明。


一開始我認為

管理者在修改前,其實非常需要一個能「瀏覽所有教室狀態」的總覽入口,這是我最初設計中沒有想到的部分,後來跟組內討論後,才發現確實需要。

進入到此畫面後,我們可以看到所有教室的教室類型及是否可飲食的資訊;右下角的「+」按鈕,也可以提供管理方便去新增教室,這樣既能確定教室有正式新增在目前 App 上,也能避免疏忽。

UI 畫面 操作流程
UI 畫面 操作流程

實際讀完後,AllClassroomsActivity.kt 負責什麼

和 Hermes Agent 一起重讀 AllClassroomsActivity.kt 後, 它是管理者「所有教室狀態」的頁面。負責顯示所有教室清單、顯示每間教室的名稱/類型/是否可飲食、讓管理者點選某間教室看更完整課表狀態,並提供新增教室功能。

剖析程式碼後,重點可以分為以下三大核心機制:

1. 資料模型轉換

viewModel.allSchedules.observe(this, Observer { schedules ->
    val classroomInfos = schedules.map { schedule ->
        ClassroomInfo(
            schedule.classroom,
            schedule.classroomType ?: "未知",
            schedule.classroomType == "Normal"
        )
    }.sortedBy { it.name }
    adapter.updateData(classroomInfos)
})
  • 技術細節:把資料庫 Entity ClassroomSchedule 轉成 UI 顯示用的 ClassroomInfo,只保留畫面需要的教室名稱、類型、是否可飲食。

2. 檢查名稱格式和重複與新增

// 1. 正規表達式(Regex)格式驗證
val name = classroomNameInput.text.toString().trim().uppercase()

if (name.isEmpty() || !name.matches(Regex("^(SF|ES)\\d+$"))) {
    Toast.makeText(this, "教室名稱格式不符 ...", Toast.LENGTH_LONG).show()
    return@setOnClickListener
    

// 2. 非同步查重機制
lifecycleScope.launch {
    val exists = viewModel.checkExists(name)
    if (exists) {
        Toast.makeText(this@AllClassroomsActivity, "提醒:已經有這間教室了,不進行新增", Toast.LENGTH_LONG).show()
    } else {
        viewModel.insert(name, type)
        Toast.makeText(this@AllClassroomsActivity, "教室 $name 新增成功", Toast.LENGTH_SHORT).show()
        dialog.dismiss()
    }
}
  • 技術細節:新增教室名稱必須符合 SF 或 ES 開頭,後面接數字,例如 SF304、ES201。新增前會先檢查教室是否已存在;存在就不新增,不存在才呼叫 insert()。

3. 初始狀態建立

fun insert(classroomName: String, classroomType: String) = viewModelScope.launch {
    val newSchedule = ClassroomSchedule(
        classroom = classroomName,
        classroomType = classroomType,
        mon1 = null, mon2 = null, ...,
        fri8 = null
    )
    repository.insert(newSchedule)
}
  • 技術細節:新教室會建立一筆新的 ClassroomSchedule。所有時段都是 null,代表初始狀態沒有課;查詢頁用 != "X" 判斷可用,所以這些時段會被視為空閒。

它接在哪一條流程上

所有教室狀態總覽:

ManagerActivity
「點擊所有教室狀態」
        ↓
AllClassroomsActivity
        ↓
Repository.getAllSchedules()
「取得所有教室課表」
        ↓
ClassroomSchedule
「取得所有教室資料」
        ↓
轉換成 ClassroomInfo
「整理列表需要的資料」
        ↓
AllClassroomsAdapter
「顯示所有教室總覽」

新增教室的流程

AllClassroomsActivity
「點擊新增教室」
        ↓
輸入教室資料
        ↓
檢查教室名稱格式
        ↓
檢查教室是否已存在
        ↓
檢查通過?
   ┌────┴────┐
   │         │
  是         否
   │         │
   ▼         ▼
建立       顯示錯誤提示
ClassroomSchedule
   │
   ▼
Repository / DAO
「新增教室資料」
   │
   ▼
Room Database
「儲存新教室」

Hermes Agent 幫我檢查出的重點

AllClassroomsActivity 是管理者瀏覽所有教室狀態的頁面。它從 ManagerActivity 的「所有教室狀態」入口進入,使用 all_classrooms.xml,畫面包含所有教室列表、離開按鈕與新增教室浮動按鈕。

  • Activity 透過 AllClassroomsViewModel 從 Repository 取得所有 ClassroomSchedule,並在 observe 時轉成 ClassroomInfo:名稱取自 classroom,類型取自 classroomType,是否可飲食則用 classroomType == "Normal" 推導。
  • 新增教室時,程式會跳出 Dialog,要求輸入教室名稱與選擇教室類型;名稱必須符合 SF 或 ES 開頭加數字,並且不能已經存在。新增成功後會 insert 一筆新的 ClassroomSchedule,所有課表時段初始都是 null,代表沒有課,查詢時會被視為可用。

這個檔案帶出的維護觀察

  1. Destructive Migration 與重建:大樓代碼是硬編碼,管理端手動新增的自訂教室並不存在於初始的 CSV 檔案中。雖然在一般的 onOpen 重新載入時不一定會直接刪除這類自訂資料,但一旦遇到資料表升級(destructiveMigration)或資料庫重建,系統只會倒回初始 CSV,手動新增的教室會直接消失。
  2. 主鍵衝突時的 REPLACE 無預警覆蓋:資料寫入時採用 Room 的 REPLACE 策略,一旦手動新增的教室名稱(Primary Key)與 CSV 原始資料撞名,系統會在沒有任何警示或確認的情況下,直接用 CSV 的初始資料,強制覆蓋掉手動修改的內容,且完全沒有任何警告機制。
  3. 職責分離不明確: AllClassroomsActivity.kt 裡同時有 Activity、ViewModel、Factory,小專案可行,但如果未來需要再發展可能需要拆分。

小結

讀到 AllClassroomsActivity 時,我看到了管理者功能裡的第三條主線:瀏覽所有教室狀態。這頁不是只列出教室名稱,而是把每間教室轉成 ClassroomInfo,顯示名稱、教室類型與是否可飲食。這讓管理者可以從總覽頁快速知道目前資料庫裡有哪些教室。

這頁也提供新增教室功能。新增時會檢查名稱格式,必須是 SF 或 ES 開頭並接數字,也會檢查是否已經存在。新增成功後,程式會建立一筆新的 ClassroomSchedule,並把所有時段設為 null。這代表新教室預設所有時段都沒有課,會被查詢頁視為可用。

這裡也延續了前面看到的技術債:大樓代碼仍然硬編碼在程式裡,而且新增資料和 CSV 預載資料之間的關係需要小心。若資料庫重建或 migration 策略改變,管理者新增的資料可能會有遺失風險。

一句話重點總結:

AllClassroomsActivity 顯示所有教室的狀態總覽,並提供新增教室功能;新增教室會建立一筆所有時段為 null 的 ClassroomSchedule。


下一篇預告:一張教室卡片背後,藏著完整課表的入口

掌握了「所有教室狀態」的頁面架構與新增防呆機制後,畫面上那一張張資訊卡片,究竟是如何被優雅地繪製與組裝的?

下一篇,我們將焦點對準 AllClassroomsAdapter.kt:拆解 Adapter 如何將精簡後的 ClassroomInfo 綁定至視圖,卡片會顯示名稱、類型、是否可飲食;並把單純的卡片點擊化為導航入口,帶領使用者一鍵跳轉至「完整課表頁」。


上一篇
Day 22 | 選到教室之後,資料怎麼被查出來並修改?(下)
系列文
從 Vibe Coding 到可維護專案:用 Hermes Agent 重讀 RoomRush Android 專題 共 23 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言