· 7,225 chars · 8 min Updated

邏輯介紹

角色與使用情境

角色與使用情境

角色可以做什麼看不到什麼主要入口
未登入訪客看到登入提示所有提案、公告、通知、留言登入面板
校內一般使用者看公共議題、設備提案、公告;建立提案;留言;附議公共議題其他人的學生權益維護提案、其他人的通知、其他人的私有作者資料提案看板、公告頁
提案作者管理自己的提案;在「我的提案」看到自己發過的內容其他人的私密提案我的提案分頁、提案詳情
公共議題附議者對公共議題附議或取消附議其他使用者是否附議公共議題卡片、提案詳情
管理員調整提案狀態、建立公告、刪除不適合內容、看平台統計管理員狀態選單、公告管理、Dashboard

使用者功能總覽

功能使用者會看到的行為背後邏輯
Google 校內登入只能用已驗證的校內 Google 帳號登入前端與 Cloud Functions 都檢查 Google provider、email verified、指定網域
提案分類分頁公共議題、學生權益維護、設備、我的提案路由 /issues/:filter 控制目前分頁,不靠單一畫面內部狀態硬切
提案看板卡片與列表模式切換、搜尋、分頁Firestore 即時訂閱加上前端分頁與搜尋狀態
新增提案填標題、內容、分類,可插入圖片送出時統一呼叫 backendAction,由後端驗證與寫入
公共議題附議按下附議後數字立即更新,失敗會回復前端 optimistic UI,後端 transaction 防止重複附議
提案留言在提案詳情內留言,可附圖片留言讀寫都經 Cloud Functions,Firestore client 不直接讀寫留言集合
狀態通知狀態變更、留言、附議達標會通知user\_notifications 只讓收件人讀取,最多保留最新 15 則
公告校內使用者可看公告、留言、按讚公告由管理員建立,互動也走 callable
管理員 Dashboard看使用者、提案、留言、附議與分類統計後端累加平台統計,Dashboard callable 僅 admin 可取
Notion 備份提案、公告與互動事件可同步到 NotionCloud Tasks 排隊,失敗可重試並記錄狀態

提案分類與差異

分類適合內容誰看得到是否可附議作者顯示期限邏輯
公共議題影響多數學生的制度、環境、流程問題校內使用者都可看是,目標 50 人一般使用者看不到作者真實資訊;本人與管理員可看私有作者資料14 天內未達 50 人會自動轉為「未通過」;達標後有 7 天回覆期限
學生權益維護個別權益、敏感事件、需要私下處理的問題只有作者本人與管理員可看只在本人與管理員情境可辨識建立後直接進入待回覆流程,回覆期限 7 天
設備設備損壞、空間問題、修繕需求校內使用者都可看可顯示校內作者資訊建立後直接進入待回覆流程,回覆期限 7 天
我的提案使用者自己發出的全部提案只列出目前登入者自己的提案依原分類規則使用者可以知道哪些是自己送出的用路由 filter 顯示,不是獨立分類

提案狀態

狀態代碼顯示文字代表意義誰能造成狀態變化
pending未回覆新提案等待管理方處理;公共議題可能仍在附議期使用者建立提案後產生
processing處理中管理方已接手處理管理員
auto\-rejected未通過公共議題附議期到期但未達門檻排程函式或維護 callable
infeasible無法實行管理方判定目前無法執行管理員
completed已完成已處理或完成回覆管理員

提案生命週期

階段公共議題學生權益維護設備
建立檢查標題、內容、分類、圖片;產生附議期限檢查標題、內容、分類、圖片;標記私密可見性檢查標題、內容、分類、圖片
初始狀態pendingpendingpending
附議可附議,可取消,重複附議被後端拒絕不開放不開放
達標達 50 人後記錄 support\_met\_at,設定回覆期限不適用不適用
未達標14 天後自動轉為 auto\-rejected不適用不適用
管理員處理可改為處理中、無法實行、已完成可改為處理中、無法實行、已完成可改為處理中、無法實行、已完成
通知留言、狀態更新、附議達標都可通知留言與狀態更新通知作者留言與狀態更新通知作者
備份Notion 事件同步Notion 事件同步,但依資料設計保護私密資訊Notion 事件同步

權限模型總表

動作未登入校內使用者作者本人管理員
讀取公共議題
讀取學生權益維護提案
讀取設備提案
新增提案
刪除自己的提案
調整提案狀態
附議公共議題
附議學生權益維護或設備
讀取留言透過 callable 依提案權限讀取
新增留言透過 callable 依提案權限新增
讀取通知只能讀自己的只能讀自己的只能讀自己的
建立公告
讀取 Dashboard

登入與校內身分驗證

檢查項目前端用途後端用途原因
Google 登入 provider引導使用者用正確方式登入validateAuthAndDomain 拒絕非 Google provider避免用其他 provider 繞過校內帳號政策
Email 已驗證顯示登入狀態前先驗證callable 每次操作都驗證 token避免未驗證 email 被當成有效校內身分
Email 網域顯示校內網域限制比對 ALLOWED\_DOMAIN平台只服務指定學校網域
管理員 email顯示管理 UI比對 ADMIN\_EMAILS管理員身份不靠前端判斷
UID訂閱自己的通知與提案寫入作者、附議、留言紀錄保持每個操作可追蹤

資料讀寫邊界

資料前端可直接讀嗎前端可直接寫嗎實際寫入方式
issues可讀,但學生權益維護只限作者與管理員backendAction\.createIssuedeleteIssuemoderateIssueStatus
issues/\{issueId\}/votes/\{uid\}只可讀自己的投票狀態或管理員讀取backendAction\.toggleSupportremoveSupport
private\_issue\_authors只限作者與管理員建立公共議題時由後端同步寫入
issue\_commentsbackendAction\.listCommentscreateCommentdeleteComment
announcements校內使用者可讀管理員 callable 建立、更新、刪除
announcements/\{id\}/likes/\{uid\}只可讀自己的讚狀態或管理員讀取backendAction\.toggleAnnouncementLike
announcement\_comments公告留言 callable
uploads圖片上傳、解析、刪除 callable
user\_notifications只可讀自己的通知後端事件建立通知
platform\_stats管理員 Dashboard callable

為什麼所有寫入都走 Cloud Functions

風險如果讓前端直接寫入會怎樣現在的做法
偽造作者使用者可能改掉 author\_uid 或管理欄位後端從 Firebase Auth token 取 uid/email/name
偽造管理員前端 UI 隱藏不代表安全後端每次 callable 都比對 ADMIN\_EMAILS
重複附議同一人可能送出多次寫入Firestore transaction 與 votes 子集合防重
私密提案外洩client query 寫錯就可能列出敏感資料Firestore Rules 先擋,再由查詢服務按分類取資料
圖片任意讀寫Storage URL 若公開會擴散Storage 規則拒絕 uploads 直接讀寫,只用短效 signed URL
Notion 同步失敗直接同步容易讓使用者操作卡住Cloud Tasks 排隊、重試、限速

圖片與 Markdown

項目設計
圖片格式上傳前端先壓縮,後端接受 WebP
單張大小後端限制最大 2 MB
預覽圖後端用 sharp 產生最大 480px preview
Markdown 語法支援 `![alt
Storage 權限/uploads/\*\* 不允許前端直接 read/write/delete
圖片讀取前端解析 srp\-upload:// 後,透過 callable 換短效 signed URL
signed URL 有效期15 分鐘
可見性schoolprivate,由 registry 與權限 helper 判斷
刪除刪提案、留言或公告時,後端清理關聯圖片

公共議題附議邏輯

規則說明
只限公共議題學生權益維護與設備不需要附議流程
目標人數PUBLIC\_SUPPORT\_GOAL = 50
附議期限建立後 14 天
達標效果寫入 support\_met\_at,設定 7 天回覆期限,通知作者
未達標效果排程每 60 分鐘掃描,到期且未達標轉成 auto\-rejected
防止重複votes 子集合以 uid 當文件 ID,交易內同步 support\_count
使用者狀態users\.supported\_issue\_ids 保留使用者目前附議過的提案 ID

通知與公告

模組功能權限
通知鈴鐺顯示最新通知與未讀數只讀目前登入者的通知
提案留言通知有人留言時通知相關使用者後端決定收件人
狀態變更通知管理員改狀態後通知作者後端建立
附議達標通知公共議題達標時通知作者後端建立
公告列表校內使用者可讀全站公告管理員才可新增、編輯、刪除
公告留言與讚校內使用者互動透過 callable 寫入,非 client 直寫

Notion 備份邏輯

事件類型備份內容同步方式
提案建立標題、分類、狀態、作者資訊、內容建立或更新 Notion page
提案狀態變更舊狀態、新狀態、時間追加事件紀錄
提案留言留言摘要、圖片 block追加到 Notion
公告建立或更新公告標題、內容、互動狀態建立或更新 Notion page
公告留言或讚互動事件追加到 Notion
刪除事件將 Notion 對應頁或事件標記處理透過 event handler

Notion 同步不直接卡住使用者操作。後端會先寫入 Firestore,再把同步事件丟進 Cloud Tasks。任務最多重試 5 次,限速為每秒 1 個、同時最多 2 個,避免打爆 Notion API 或因短暫失敗造成資料遺失。

主要資料集合

Collection主要用途讀取者寫入者
issues提案主資料校內使用者依分類與作者權限讀取Cloud Functions
private\_issue\_authors公共議題私有作者資訊作者本人、管理員Cloud Functions
issue\_comments提案留言只能透過 callable 取得Cloud Functions
announcements公告校內使用者Cloud Functions
announcement\_comments公告留言只能透過 callable 取得Cloud Functions
users使用者附議 ID、頭像快取等本人Cloud Functions
uploads圖片 registry不開放 client 直接讀Cloud Functions
user\_notifications站內通知收件人本人Cloud Functions
platform\_stats平台成果統計管理員 callableCloud Functions
notion\_pages / notion\_issue\_pagesFirestore 與 Notion 頁面對應後端內部Cloud Functions
notion\_sync\_jobsNotion 同步任務狀態後端內部Cloud Functions
rate\_limits發文與留言限流 bucket後端內部Cloud Functions

使用者常見問題

問題回答
為什麼我看不到別人的學生權益維護提案?這個分類是私密用途,只能看到自己發出的提案;如果目前是空的,代表你還沒有在這個分類送出提案。
為什麼公共議題看不到作者?公共議題希望降低提案者被針對的風險,因此一般使用者看議題內容與附議狀態即可,作者資訊由後端保存給本人與管理員使用。
為什麼設備提案不能附議?設備問題通常是明確修繕或回報流程,不需要用附議達標來判斷是否處理。
為什麼圖片有時需要重新載入?圖片不是永久公開 URL,而是短效 signed URL;過期後前端會再向後端解析。
為什麼送出失敗時圖片不會留下公開連結?圖片先進 registry,送出失敗或刪除內容時後端會清理,降低孤兒檔案與外洩風險。
管理員是不是可以在前端直接改資料?不行。前端只能呼叫後端 action,真正的 admin 判斷在 Cloud Functions。
提案內容支援 Markdown 嗎?支援,並會用 DOMPurify 過濾渲染後 HTML,降低 XSS 風險。

前端模組責任

模組責任
src/router將提案、公告、管理員頁面路由化;提案 filter 由 URL 控制
src/views頁面入口,只組合資料流程與主要元件
src/componentsUI 呈現與互動元件,避免直接寫 Firestore 邏輯
src/components/ui純展示共用元件,不 import composable 或 service
src/composables狀態、表單、訂閱、分頁、搜尋、對話框、通知等邏輯
src/servicesFirestore 讀取與 Cloud Functions callable 包裝
src/lib無 Vue 相依的純函式,例如 Markdown 圖片解析、日期格式化、搜尋正規化
src/constants分類、狀態、路由選項
src/typesTypeScript 型別,作為資料模型文件

後端模組責任

模組責任
functions/src/index\.ts只匯出部署入口
functions/src/entrypoints/backend\-action\.ts單一 callable router,依 action 分派 handler
functions/src/coreFirebase Admin 初始化、環境變數、身分驗證、限流
functions/src/issues提案建立、刪除、留言、附議、狀態管理、過期同步
functions/src/announcements公告 CRUD、公告留言、讚、Notion retry
functions/src/uploads圖片建立、解析、刪除、Storage 與 registry 權限
functions/src/notifications通知建立與已讀時間
functions/src/stats平台統計與 Dashboard
functions/src/notionNotion client、Markdown 轉 block、事件同步與 page mapping
functions/src/scheduled每小時自動駁回、每日清理 Secret Manager 舊版本

一句話流程圖

flowchart TD
  A["校內 Google 登入"] --> B["選擇提案分類"]
  B --> C["送出提案與圖片"]
  C --> D["Cloud Functions 驗證與寫入"]
  D --> E{"公共議題?"}
  E -->|是| F["14 天附議期"]
  F --> G{"達 50 人?"}
  G -->|是| H["設定 7 天回覆期限並通知"]
  G -->|否| I["排程自動轉未通過"]
  E -->|否| J["直接等待管理員回覆"]
  H --> K["管理員處理狀態"]
  I --> K
  J --> K
  K --> L["通知使用者並同步 Notion"]

相关推荐