Files
ROLAC/docs/PLANNING.md
T
2026-05-29 22:27:06 -07:00

1530 lines
62 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ROLAC Church Management System — Project Planning
**教會:** River Of Life Christian Church In Arcadia (ROLAC)
**規模:** 50100 人
**文件版本:** v0.3 (2026-05-29)
---
## 目錄
1. [專案目標](#1-專案目標)
2. [系統模組總覽](#2-系統模組總覽)
3. [模組詳細規劃](#3-模組詳細規劃)
- 3.1 [教友管理 (Member Management)](#31-教友管理-member-management)
- 3.2 [權限控管 & 多角色 (RBAC)](#32-權限控管--多角色-rbac)
- 3.3 [小組架構 (Cell Groups / Ministry Structure)](#33-小組架構-cell-groups--ministry-structure)
- 3.4 [服事表 (Service Roster)](#34-服事表-service-roster)
- 3.5 [教會首頁 CMS](#35-教會首頁-cms)
- 3.6 [奉獻追蹤 (Giving / Donations)](#36-奉獻追蹤-giving--donations)
- 3.7 [年度收據 (Annual Giving Statement)](#37-年度收據-annual-giving-statement)
- 3.8 [Audit Log](#38-audit-log)
- 3.9 [報表 (Reports)](#39-報表-reports)
- 3.10 [Ministry Scope](#310-ministry-scope)
- 3.11 [未來 AI 整合](#311-未來-ai-整合)
4. [角色與權限矩陣](#4-角色與權限矩陣)
5. [資料模型草圖](#5-資料模型草圖)
6. [技術架構建議](#6-技術架構建議)
7. [開發階段規劃 (Roadmap)](#7-開發階段規劃-roadmap)
8. [開放議題 & 待決策](#8-開放議題--待決策)
---
## 1. 專案目標
| 目標 | 說明 |
|------|------|
| 數位化教友資料 | 取代紙本或分散的試算表,集中管理教友資訊 |
| 透明化財務 | 讓奉獻記錄可查詢、可稽核,年底產生合規收據 |
| 強化小組連結 | 清楚呈現小組架構,追蹤出席與服事參與 |
| 簡化行政工作 | 服事排班、通知、報表自動化,減少人工作業 |
| 對外溝通 | CMS 管理教會網站,發佈消息/活動 |
| 安全與稽核 | 完整 Audit Log,細緻的角色權限,保護敏感資料 |
| 可擴展性 | 為未來 AI 功能(關懷提醒、智能報表)預留介面 |
---
## 2. 系統模組總覽
```
ROLAC CMS
├── 公開前台 (Public Website — rolac.org)
│ └── 教會首頁 CMS(雙語)
└── 後台管理 (Admin Portal — app.rolac.org)
├── 教友管理
├── 小組架構 & Ministry Scope
├── 主日出席記錄
├── 服事表
├── 敬拜歌曲庫
├── 代禱事項
├── 奉獻追蹤(含線上奉獻)& 年度收據
├── 支出追蹤 & 報銷
├── 月結對帳
├── 報表
├── Audit Log
├── 權限控管 (RBAC)
└── [未來] AI 整合
```
---
## 3. 模組詳細規劃
### 3.1 教友管理 (Member Management)
**核心資料欄位**
| 欄位群 | 欄位 |
|--------|------|
| 基本資料 | 姓名 (中/英)、生日、性別、照片 |
| 聯絡資訊 | 電話、Email、地址 |
| 教會狀態 | 會員/訪客/前會員、受洗日期、入會日期、離會原因 |
| 家庭關係 | 家庭單元 ID、配偶、子女 |
| 小組歸屬 | 所屬小組、負責牧者/組長 |
| 服事歷史 | 參與服事記錄 |
| 自定義標籤 | 關懷需求、恩賜標籤、語言偏好 (中/英) |
**功能清單**
- 新增 / 編輯 / 軟刪除 教友資料
- 家庭單元管理(一個家庭多位成員)
- 進階搜尋與篩選(按小組、狀態、生日月份…)
- 生日/週年紀念提醒
- 匯入 / 匯出 CSV
- 個人資料變更歷史(誰改了什麼)
---
### 3.2 權限控管 & 多角色 (RBAC)
**設計原則**
- 最小權限原則:每個角色只能存取完成工作所需的資源
- Ministry Scope:部分角色僅能看到自己負責的 Ministry 資料
- 細粒度到 Resource + Action 層級
**預設角色(見第 4 節矩陣)**
> ROLAC 為靈糧堂體制,無長老制。
| 角色 | 中文說明 | 層級 |
|------|----------|------|
| `super_admin` | 系統管理員,全權存取 | 系統 |
| `pastor` | 牧師,全覽教友與財務摘要 | 牧者 |
| `board_member` | 理事,教會治理委員 | 治理 |
| `coworker_chair` | 同工會主席,統籌各事工領袖 | 領導 |
| `ministry_leader` | 事工領袖,僅限自身 Ministry 範圍 | 事工 |
| `district_leader` | 區長,管理轄下多個小組 | 小組 |
| `cell_leader` | 小組長,僅限自身小組 | 小組 |
| `coworker` | 同工,參與指定事工的一般同工 | 同工 |
| `finance` | 財務同工,管理奉獻與支出報表 | 功能 |
| `secretary` | 行政秘書,管理教友資料與排班 | 功能 |
| `worship_leader` | 敬拜領袖,管理歌曲庫與敬拜歌單(Phase 暫緩)| 功能 |
| `member` | 一般教友,查看個人資料與服事表 | 教友 |
| `visitor` | 訪客,僅限公開頁面 | 教友 |
---
### 3.3 小組架構 (Cell Groups / Ministry Structure)
**資料結構(樹狀)**
```
教會 (Church — River Of Life Christian Church In Arcadia)
└── Ministry (事工部門)
└── Cell Group (小組)
└── Member (教友)
```
**ROLAC 實際事工部門(初始種子資料)**
| # | 英文名稱 | 中文名稱 | 主要職責 |
|---|----------|----------|----------|
| 1 | Administration | 行政 | 教會行政、秘書、文件管理 |
| 2 | Preaching | 講道 | 主日講道、查經帶領 |
| 3 | Emcee | 司會 | 主日流程主持 |
| 4 | Worship | 敬拜 | 敬拜帶領、詩歌、樂器 |
| 5 | PPT / Media | PPT/影音 | 投影片製作、直播、錄影 |
| 6 | Sound | 音控 | 現場音響、混音 |
| 7 | Facility | 場地組 | 場地佈置、清潔、設備 |
| 8 | Hospitality | 招待 | 門口招待、訪客關懷、奉獻袋收集 |
| 9 | Children | 兒牧 | 兒童主日學、托兒 |
| 10 | Catering | 餐飲 | 每週愛宴、餐飲採購 |
> 這 10 個 Ministry 為系統初始種子資料(`DbInitializer`),可在後台新增 / 修改。
**功能清單**
- 建立 / 管理 Ministry 與小組層級
- 指派組長與副組長
- 成員歸屬管理(一個教友可屬於多個 Ministry)
- 出席記錄(每次小組聚會)
- 成員轉移與歷史追蹤
- 小組狀態(活躍 / 暫停 / 解散)
- Ministry Scope 權限綁定
---
### 3.3b 主日出席記錄 (Sunday Service Attendance)
> **記錄方式(已確定):** 招待同工(Greeting Team)統計總人數後,由秘書或招待組長手動輸入。
**統計分類**
| 類別 | 說明 |
|------|------|
| 大人 (Adults) | 成年會眾人數 |
| 青少年 (Teenagers) | 青少年人數 |
| 兒童 (Children) | 兒童主日學人數 |
| 合計 (Total) | 系統自動加總 |
**資料模型**
```
SundayAttendance
├── Id
├── ServiceDate (日期)
├── ServiceType (主日崇拜 / 特別活動 / 節日崇拜)
├── AdultCount
├── TeenCount
├── ChildrenCount
├── TotalCount (computed: Adult + Teen + Children)
├── Notes (備註,例:聖誕主日特別人數)
├── RecordedByUserId (誰輸入)
└── CreatedAt / UpdatedAt
```
**功能清單**
- 每週輸入三個類別人數(簡單表單)
- 合計自動計算
- 月 / 季 / 年 出席趨勢圖
- 出席率計算(以平均或最高值為基準)
- 節日 / 特別活動標記
- 匯出 CSV 供外部報表使用
---
### 3.4 服事表 (Service Roster)
**ROLAC 主日服事槽位(初始種子資料)**
每個槽位屬於一個 Ministry,一次服事可填入一個或多個同工。
| Ministry | 服事槽位 | 人數 | 頻率 |
|----------|---------|------|------|
| 講道 | 講員 Preacher | 1 | 每週 |
| 司會 | 主持人 Emcee | 1 | 每週 |
| 敬拜 | 敬拜帶領 Worship Leader | 1 | 每週 |
| 敬拜 | 敬拜團員 Worship Team | 25 | 每週 |
| 敬拜 | 司琴 Pianist | 1 | 每週 |
| PPT/影音 | PPT 操作 Slide Operator | 1 | 每週 |
| PPT/影音 | 攝影 / 直播 Livestream | 01 | 每週 |
| 音控 | 音控 Sound Engineer | 1 | 每週 |
| 場地組 | 場地佈置 Setup | 23 | 每週 |
| 場地組 | 場地收拾 Teardown | 23 | 每週 |
| 招待 | 門口招待 Greeter | 23 | 每週 |
| 招待 | 奉獻袋 Offering Collection | 2 | 每週 |
| 兒牧 | 兒童主日學老師 | 1–2 | 每週 |
| 兒牧 | 兒童助教 Assistant | 01 | 每週 |
| 餐飲 | 愛宴負責人 Catering Lead | 1 | 每週 |
| 餐飲 | 愛宴協助 Catering Help | 24 | 每週 |
**功能清單**
- 定義服事槽位(綁定 Ministry,雙語名稱)
- 每週排班(從教友庫中指派到各槽位)
- 週期性排班範本(每週 / 隔週 / 每月輪換)
- 自動衝突檢查(同一人同一主日多個衝突槽位)
- 缺席替換流程(申請 → 系統建議替補 → 確認)
- Email / Push Notification 服事提醒(服事前 2 天)
- 公開服事表嵌入網站(確認後才公開)
- 服事統計(每人參與頻率)
---
### 3.5 教會首頁 CMS
> **雙語範圍(已確定):** 網站前台完整支援 **英文 (EN) + 繁體中文 (zh-TW)** 切換。
> 所有可見文字均需提供兩個語言版本,包含 SEO meta 欄位。
**頁面 / 內容類型**
| 類型 | 說明 |
|------|------|
| 首頁 Hero | 輪播橫幅、主題文字(雙語) |
| 關於我們 | 教會簡介、異象、牧師介紹(雙語) |
| 主日資訊 | 時間、地點、Google Map(地址雙語) |
| 消息公告 | 可分類標籤、發佈排程(標題 + 內文雙語) |
| 活動日曆 | 事件列表,可連結報名表單(雙語) |
| 媒體 | 主日講道影片/音訊嵌入(YouTube/Podcast)(標題雙語) |
| 奉獻頁面 | 線上奉獻說明(雙語) |
| 聯絡表單 | 訪客詢問,通知至指定 Email(表單標籤雙語) |
**雙語內容資料模型**
CMS 所有可編輯的文字欄位,一律使用平行欄位儲存:
```
CmsContent
├── Id
├── PageSlug (e.g. "home", "about", "announcements")
├── SectionKey (e.g. "hero.title", "hero.subtitle")
├── ContentType (Text | RichText | ImageUrl | Url)
├── ValueEn ← 英文版本
├── ValueZh ← 繁體中文版本
├── IsPublished
├── PublishAt (排程發佈)
├── UpdatedByUserId
└── UpdatedAt
Announcement
├── Id
├── TitleEn / TitleZh
├── BodyEn / BodyZh (Rich text)
├── Category
├── IsPublished
├── PublishAt
├── ExpiresAt
└── CoverImageUrl
Event
├── Id
├── TitleEn / TitleZh
├── DescriptionEn / DescriptionZh
├── Location (共用,或 LocationEn / LocationZh)
├── StartAt / EndAt
├── RegistrationUrl
└── CoverImageUrl
```
**後台 CMS 編輯器 UIAdmin App**
每個可編輯欄位都採用**並排雙欄**編輯介面:
```
┌──────────────────────┬──────────────────────┐
│ English │ 繁體中文 │
│ ───────────────── │ ───────────────── │
│ [Welcome to ROLAC] │ [歡迎來到生命之河] │
└──────────────────────┴──────────────────────┘
```
- 兩欄同時顯示,編輯器可獨立輸入
- 儲存時驗證:若只填一種語言,提示警告(但允許儲存)
- 預覽模式可切換 EN / zh-TW 即時檢視
**前台語言切換 UI**
- Header 右上角 `EN | 中` 切換按鈕
- 語言偏好存於 `localStorage`(訪客也記住)
- URL 策略:使用 Angular `i18n` route prefix 或 query param,二擇一:
- 方案 A(推薦):`rolac.org/en/about` vs `rolac.org/zh/about` — 對 SEO 最友善
- 方案 B`rolac.org/about?lang=zh` — 實作較簡單
**SEO 雙語支援**
每個頁面在 `<head>` 中加入:
```html
<link rel="alternate" hreflang="en" href="https://rolac.org/en/about" />
<link rel="alternate" hreflang="zh-TW" href="https://rolac.org/zh/about" />
<link rel="alternate" hreflang="x-default" href="https://rolac.org/en/about" />
```
- Meta title / description 依當前語言動態注入
- 使用 Angular `Meta` service 或 `ngx-seo`
**技術需求**
- 富文本編輯器:**Quill** 或 **TipTap**Angular 皆有 wrapper
- 圖片上傳 → Azure Blob `cms/images/`
- 預覽草稿後才發佈
- 發佈排程(`PublishAt` 欄位 + 背景排程 Job
---
### 3.6 奉獻追蹤 (Giving / Donations)
**奉獻類型**
- 什一奉獻 (Tithe)
- 感恩奉獻 (General Offering)
- 特別奉獻 (Special/Project,可自訂名稱)
- 慣例奉獻 (Pledge)
**支付方式(完整)**
| 方式 | 類型 | 狀態 | 說明 |
|------|------|------|------|
| **現金 (Cash)** | 線下手動 | ✅ Phase 1 | 信封號碼(選填)、匿名旗標 |
| **支票 (Check)** | 線下手動 | ✅ Phase 1 | 支票號碼 |
| **Zelle** | 線下手動 | ✅ Phase 1 | 交易參考碼,無 API |
| **PayPal (手動)** | 線下手動 | ✅ Phase 1 | Transaction ID 手輸 |
| **Stripe (線上)** | 線上支付 | 🔜 Phase 4 | 信用卡 / Apple Pay / Google Pay |
| **PayPal Checkout (線上)** | 線上支付 | 🔜 Phase 4 | PayPal 按鈕嵌入 |
**資料模型重點欄位**
```
Giving
├── Id
├── MemberId (可為 null → 匿名 / 訪客線上奉獻)
├── GiverName (線上匿名奉獻者填寫的名字,可 null)
├── GiverEmail (線上奉獻用,發確認信)
├── GivingDate
├── Amount (decimal, 原始金額)
├── FeeAmount (decimal, 手續費,預設 0)
├── NetAmount (computed: Amount FeeAmount)
├── GivingCategoryId (Tithe / Offering / Special…)
├── PaymentMethod (Cash | Check | Zelle | PayPal | Stripe | PayPalCheckout)
├── PaymentSource (Manual | OnlineStripe | OnlinePayPal)
├── ReferenceNumber (支票號 / Zelle ref / Stripe PaymentIntent ID / PayPal txn)
├── EnvelopeNumber (現金信封號碼,選填)
├── IsAnonymous (bool)
├── FundProjectId (特別奉獻專案,可 null)
├── IsRecurring (bool, 定期奉獻)
├── RecurringScheduleId (FK → GivingRecurringSchedule,可 null)
├── Notes
├── RecordedByUserId (手動輸入者;線上奉獻為 null)
└── StripeEventId (防重複,Webhook idempotency key)
GivingRecurringSchedule ← 定期線上奉獻
├── Id
├── MemberId
├── Amount
├── GivingCategoryId
├── Frequency (Weekly | BiWeekly | Monthly)
├── NextChargeDate
├── StripeSubscriptionId
├── Status (Active | Paused | Cancelled)
└── CreatedAt
```
**功能清單**
- 手動記錄奉獻(含現金/支票/Zelle/PayPal
- **主日奉獻袋批次輸入**Phase 1,見 §3.6c
- 線上奉獻頁(Phase 4,見 §3.6b
- 定期自動奉獻(Stripe SubscriptionPhase 4
- PayPal / Stripe 手續費分開記錄,IRS 收據用 **NetAmount**
- 奉獻目標 / 專案進度條
- 個人奉獻歷史(本人可查)
- 月結對帳報表
---
#### 3.6c 主日奉獻袋批次輸入 (Sunday Offering Batch Entry — Phase 1)
**業務場景**
主日聚會結束後,財務同工收到奉獻袋,需要逐一開袋,將每筆現金信封或支票對應到教友姓名,登入系統記錄,年底才能產生個人奉獻收據。
**工作流程**
```
財務同工開啟「主日奉獻錄入」
選擇日期(預設今天)
┌───────────────────────────────────────┐
│ 逐筆快速錄入介面 │
│ ───────────────────────────────── │
│ 搜尋教友:[_______________] ← 即時搜尋 │
│ 奉獻類型:[Tithe ▼] │
│ 支付方式:[● 現金 ○ 支票] │
│ └─ 現金:信封號碼 [___] (選填) │
│ └─ 支票:支票號碼 [___] (必填) │
│ 金額: [$________] │
│ 備注: [_______________] (選填) │
│ │
│ [+ 新增] [匿名奉獻] │
│ ───────────────────────────────── │
│ 已錄入 12 筆 | 小計:$1,250.00 │
└───────────────────────────────────────┘
所有信封錄入完成
對帳確認頁(顯示今日所有筆數 + 總計)
財務同工核對實際金額 = 系統合計
確認送出 → 系統 Bulk Insert
```
**UI 設計要點**
| 要點 | 說明 |
|------|------|
| 教友搜尋 | 即時搜尋(姓名中英文 / 拼音),顯示照片方便確認 |
| 鍵盤優先 | Tab 鍵在欄位間切換,Enter 新增,不需滑鼠 |
| 支付方式切換 | 選「支票」才出現支票號碼欄;選「現金」才出現信封號欄 |
| 匿名奉獻 | 一鍵匿名,不需搜尋教友,仍記錄金額供帳目平衡 |
| 即時小計 | 每新增一筆,當日累計金額即時更新 |
| 對帳視圖 | 最後一步顯示完整清單,供財務核對實際鈔票 / 支票 |
| 可編輯 | 送出前可點任一筆修改或刪除 |
| 送出後修改 | 需財務角色才可編輯,修改記錄進 Audit Log |
**資料模型補充**
```
OfferingSession ← 每次主日錄入為一個 Session
├── Id
├── SessionDate (主日日期)
├── Status (Open | Submitted)
├── CashTotal (computed)
├── CheckTotal (computed)
├── TotalAmount (computed)
├── SubmittedByUserId
├── SubmittedAt
└── Givings[] (關聯當次錄入的所有 Giving 記錄)
```
> **為什麼需要 OfferingSession**
> 將同一主日的所有奉獻筆數綁在一起,方便財務同工核對「這次錄入的總計」是否與實際收到的現金+支票金額吻合,在確認前可以整批修改,確認後進 Audit Log。
---
#### 3.6d 支出追蹤 & 報銷 (Expense Tracking & Reimbursement — Phase 1)
**業務場景**
| 場景 | 說明 |
|------|------|
| 代墊報銷 | 同工先自掏腰包買教會物品,事後憑收據向教會申請報帳 |
| 廠商付款 | 教會直接開支票給廠商(例:每週愛宴午餐外燴)|
| 雜支現金 | 小額現金支出(如停車費、文具)|
**支出流程**
```
【場景 A:代墊報銷】
同工提交報銷申請
(日期、金額、類別、說明、收據照片)
財務同工審核
├── 批准 → 開支票 / 現金還款給同工
│ 記錄支票號碼 + 付款日期
└── 退回 → 填寫退回原因
【場景 B:廠商直接付款(如愛宴)】
財務同工直接建立支出記錄
(廠商名稱、金額、支票號碼、日期、類別)
→ 狀態直接為「已付款」
```
**支出分類架構(兩層 + Ministry**
每筆支出標記三個維度:
```
Ministry(事工部門) × 大類(Category Group × 子項目(Sub-Category
範例:
敬拜事工 > 影音 > 設備
敬拜事工 > 影音 > 教育訓練
餐飲部門 > 餐飲 > 消耗品
餐飲部門 > 餐飲 > 出餐費用
行政 > 辦公 > 文具耗材
行政 > 人事 > 薪資
兒童事工 > 教材 > 印刷費用
```
> **設計原則:** 大類(影音、餐飲)是跨 Ministry 共用的,同一大類可出現在不同 Ministry。
> 例如:敬拜事工和兒童事工都可能有「影音 > 設備」的支出。
**資料模型**
```
ExpenseCategoryGroup ← 大類(Level 1
├── Id
├── NameEn / NameZh (影音 A/V、餐飲 Food、辦公 Office…)
├── SortOrder
└── IsActive
ExpenseSubCategory ← 子項目(Level 2
├── Id
├── ExpenseCategoryGroupId (FK → ExpenseCategoryGroup)
├── NameEn / NameZh (設備 Equipment、教育訓練 Training、出餐費用 Catering…)
├── SortOrder
└── IsActive
Expense ← 每筆支出
├── Id
├── ExpenseDate
├── Description (說明)
├── Amount
├── MinistryId (哪個事工部門承擔此支出)
├── CategoryGroupId (大類,FK → ExpenseCategoryGroup)
├── SubCategoryId (子項目,FK → ExpenseSubCategory)
├── PaymentMethod (Cash | Check | CreditCard | BankTransfer)
├── CheckNumber (若 PaymentMethod = Check)
├── PayeeType (Vendor | StaffReimbursement)
├── PayeeName (廠商名稱 或 同工姓名)
├── MemberId (若 PayeeType = StaffReimbursement → 指向同工)
├── ReceiptBlobPath (收據照片,Azure Blob: finance/receipts/)
├── Status (Draft | PendingApproval | Approved | Paid | Rejected)
├── RejectionReason
├── SubmittedByUserId
├── ApprovedByUserId
├── ApprovedAt
├── PaidAt
├── Notes
└── CreatedAt
```
**預設大類 & 子項目(種子資料,可在後台自訂)**
| 大類(EN / 中)| 子項目 | 常用於 Ministry |
|----------------|--------|-----------------|
| Equipment / 設備 | 購置 · 租借 · 維修 | 敬拜、PPT/影音、音控、場地組、兒牧 |
| Consumables / 耗材 | 電池 · 配件 · 清潔用品 · 文具 | 音控、PPT/影音、場地組、招待、行政 |
| Food & Beverage / 餐飲 | 出餐費用 · 食材採購 · 器具 · 消耗品 | 餐飲、兒牧 |
| Training / 教育訓練 | 課程費用 · 書籍 · 研討會 · 差旅 | 敬拜、PPT/影音、音控、兒牧 |
| Materials / 教材 | 印刷費用 · 手工材料 · 版權購買 | 兒牧、講道、司會 |
| Facility / 場地 | 場地租金 · 水電 · 財產保險 · 裝飾 | 場地組、行政 |
| Printing / 印刷 | 週報 · 程序單 · 海報 | 行政、招待、兒牧 |
| Missions / 宣教 | 奉獻轉帳 · 宣教士支援 · 差旅 | 行政 |
| Benevolence / 關懷 | 急難救助 · 慰問禮品 · 探訪費用 | 行政 |
| Other / 其他 | 雜支 | 所有 |
| Personnel / 人事 | 薪資 · 薪資稅費 · 員工福利 · 勞工保險 · 酬庸 · 同工進修 · 外包勞務 | 行政 |
> **分類備注:** `Facility > 財產保險` 為建築物/場地責任險;員工健保等歸 `Personnel > 員工福利`。同工代墊報銷(`StaffReimbursement`)依實際購買物選大類,薪資/福利付款才歸人事。
**Ministry × 大類 常用對應(供財務設定參考)**
| Ministry | 最常用大類 |
|----------|-----------|
| 行政 Administration | 人事 · 辦公耗材 · 印刷 · 場地 · 宣教 · 關懷 |
| 講道 Preaching | 教材 · 書籍(Training |
| 司會 Emcee | 印刷(程序單) |
| 敬拜 Worship | 設備 · 教育訓練 · 耗材 |
| PPT/影音 Media | 設備 · 耗材 · 教育訓練 |
| 音控 Sound | 設備 · 耗材 · 教育訓練 |
| 場地組 Facility | 設備 · 場地 · 耗材(清潔) |
| 招待 Hospitality | 耗材 · 印刷(程序單) |
| 兒牧 Children | 教材 · 設備 · 餐飲 · 教育訓練 |
| 餐飲 Catering | 餐飲(出餐費用 · 食材 · 器具) |
**UI 選擇流程(錄入時)**
```
選擇 Ministry → 選擇大類 → 選擇子項目
[餐飲 ▼] [餐飲 ▼] [出餐費用 ▼]
[敬拜 ▼] [設備 ▼] [購置 ▼]
```
- 大類清單不依 Ministry 過濾(保持靈活)
- 選定大類後,子項目自動篩選對應選項
- 找不到適合子項目 → 選「其他」並填說明
**狀態流轉**
```
廠商直接付款: (finance 直接建立) → Paid
同工報銷申請: Draft → PendingApproval → Approved → Paid
└─────────────────→ Rejected
```
**月底對帳(月結報表)**
這是整個財務閉環的核心:
```
月結對帳表(MonthlyStatement
期初餘額(Opening Balance $X,XXX.XX
+ 本月奉獻收入(從 Giving 表加總) $X,XXX.XX
+ 其他收入(手動補充) $ .00
− 本月支出(從 Expense 表加總) $X,XXX.XX
= 帳面期末餘額(Calculated Closing $X,XXX.XX
銀行對帳單餘額(Bank Statement Balance $X,XXX.XX
差異(Difference $ 0 ← 目標為零
```
```
MonthlyStatement
├── Id
├── Year / Month
├── OpeningBalance (手動輸入上月結餘,首月由財務設定)
├── TotalGiving (auto: SUM Giving WHERE month)
├── TotalOtherIncome (手動補充)
├── TotalExpenses (auto: SUM Expense WHERE month AND Status=Paid)
├── CalculatedClosingBalance (computed)
├── BankStatementBalance (財務同工手動輸入)
├── Difference (computed: Calculated Bank)
├── Notes
├── Status (Draft | Reconciled)
├── ReconciledByUserId
└── ReconciledAt
```
> **Phase 3 預算功能(MinistryBudget)預留欄位:**
> 分類架構現在就做好,Phase 3 只需增加 `MinistryBudget` 表,
> 按 Ministry + CategoryGroup + SubCategory 設定年度預算額,
> 對比實際 Expense 計算使用率,不需改動 Expense 結構。
**收據照片上傳(Azure Blob**
```
finance/
└── receipts/
└── {year}/{month}/{expenseId}-{filename}.jpg
```
- 使用 Capacitor Camera 拍攝,即時上傳
- 或桌機版直接上傳圖片檔
- PrivateAPI 產生 SAS URL(限財務角色存取)
**權限**
| 操作 | 角色 |
|------|------|
| 提交報銷申請 | 所有登入用戶(含 member) |
| 審核 / 批准 | `finance``super_admin` |
| 建立廠商直接付款 | `finance``super_admin` |
| 查看所有支出 | `finance``pastor``super_admin` |
| 查看人事類支出 | `finance``pastor``super_admin`(事工領袖不可見) |
| 查看自己的申請 | 提交者本人 |
| 月底對帳 | `finance``super_admin` |
---
#### 3.6b 線上奉獻頁 (Online Giving — Phase 4)
**推薦:Stripe(主要)+ PayPal 按鈕(次要)**
| 比較項目 | Stripe | PayPal |
|----------|--------|--------|
| Nonprofit 費率 | **1.5% + $0.30**(需申請 Stripe Nonprofit Rate | **1.99% + $0.49**PayPal Giving Fund for 501c3 |
| C# SDK | ✅ 官方 Stripe.net | ✅ PayPalCheckoutSdk |
| Apple Pay / Google Pay | ✅ 原生支援 | ❌ 需另外設定 |
| Webhook 可靠性 | ✅ 非常成熟 | ⚠️ 歷史上較多問題 |
| 定期奉獻 | ✅ Stripe Subscription | ⚠️ 較複雜 |
**線上奉獻頁流程(rolac.org/give**
```
訪客/教友進入 /give 頁面
選擇奉獻金(Tithe / General / Special
選擇金額(預設選項 $50 / $100 / $200 / 自訂)
選擇頻率(一次性 / 每月)
填寫姓名、Email(非登入狀態)
Stripe Payment Element(信用卡 / Apple Pay / Google Pay
Stripe 處理付款
┌──┴──┐
成功 失敗
Stripe Webhook → POST /api/webhooks/stripe
→ 自動建立 Giving 記錄(PaymentSource = OnlineStripe
→ Email 確認信給奉獻者
→ 若有 MemberId → 加入個人奉獻歷史
```
**Stripe Webhook 安全驗證(C#**
```csharp
[HttpPost("/api/webhooks/stripe")]
public async Task<IActionResult> StripeWebhook()
{
var json = await new StreamReader(Request.Body).ReadToEndAsync();
var stripeEvent = EventUtility.ConstructEvent(
json,
Request.Headers["Stripe-Signature"],
_stripeConfig.WebhookSecret // 環境變數
);
if (stripeEvent.Type == EventTypes.PaymentIntentSucceeded)
{
var intent = stripeEvent.Data.Object as PaymentIntent;
await _givingService.RecordOnlineGivingAsync(intent);
}
return Ok();
}
```
**環境變數(加入 .env**
```
STRIPE_SECRET_KEY=sk_live_...
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
PAYPAL_CLIENT_ID=...
PAYPAL_CLIENT_SECRET=...
```
---
### 3.7 年度收據 (Annual Giving Statement)
**功能清單**
- 依年度、教友產生正式奉獻收據
- PDF 格式,含教會抬頭、稅務資訊(EIN)、奉獻明細
- 批次產生(一次為所有符合條件的教友生成)
- Email 直接寄送給教友
- 收據補發 / 作廢紀錄
- 符合 IRS 501(c)(3) 格式要求
---
### 3.8 Audit Log
**記錄範圍**
| 事件類型 | 範例 |
|----------|------|
| 資料異動 | 教友資料修改、刪除 |
| 財務操作 | 奉獻新增、編輯、刪除 |
| 權限變更 | 角色指派、撤銷 |
| 認證事件 | 登入成功、失敗、密碼重設 |
| 系統設定 | 設定變更 |
**記錄欄位**
```
- timestamp (UTC)
- actor_id (who)
- actor_role
- action (CREATE / UPDATE / DELETE / LOGIN / …)
- resource_type
- resource_id
- before_snapshot (JSON)
- after_snapshot (JSON)
- ip_address
- user_agent
```
**存取控制**
- 只有 `super_admin``pastor` 可查看 Audit Log
- Audit Log 本身不可刪除(只能 Archive)
---
### 3.9 報表 (Reports)
**預設報表清單**
| 報表名稱 | 說明 | 受眾 |
|----------|------|------|
| 教友統計 | 按狀態/性別/年齡/小組分佈 | 牧師、理事、同工會主席 |
| 新人追蹤 | 過去 N 個月新訪客/受洗趨勢 | 牧師 |
| 出席報表 | 主日 & 小組出席率趨勢 | 牧師、組長 |
| 奉獻摘要 | 月/季/年 奉獻總額、按類型分佈 | 財務、牧師 |
| 服事參與 | 每人服事頻率、空缺率 | 事工負責人 |
| 關懷追蹤 | 標記需關懷教友的跟進記錄 | 牧師、組長 |
**技術需求**
- 圖表視覺化(Bar / Line / Pie
- 可匯出 CSV / PDF
- 報表可設定排程自動寄送
---
### 3.10 Ministry Scope
**概念說明**
Ministry Scope 讓 `ministry_leader``cell_leader` 只能存取屬於自己事工範圍的教友、服事與報表資料,不會看到其他事工的敏感資訊。
**實作要點**
- 每筆資源(教友、服事項目、奉獻記錄)綁定 `ministry_id`
- API 層強制過濾(後端 Row-Level Security 或 Middleware
- 跨事工存取需由上層角色(coworker_chair / pastor)代為操作
- Audit Log 記錄跨 Scope 操作
---
### 3.12 敬拜歌曲庫 (Worship Song Library)
> **用途:** 集中管理教會使用的詩歌,整合歌詞庫、敬拜排序規劃、YouTube 參考連結與教會自錄影片。
---
#### 3.12a 歌曲資料庫 (Song Catalog)
**核心欄位**
```
Song
├── Id
├── TitleEn / TitleZh
├── ArtistEn / ArtistZh (原唱/作者)
├── AlbumEn / AlbumZh (選填)
├── CcliNumber (string, 選填) ← CCLI 版權號碼
├── DefaultKey (e.g. "G", "Ab")
├── Tempo (Slow | Mid | Fast)
├── TimeSignature (e.g. "4/4", "3/4")
├── Language (EN | ZH | Bilingual)
├── Tags (string[], e.g. ["Worship","Praise","Christmas"])
├── IsActive (bool, 是否仍在使用)
├── CreatedByUserId
└── UpdatedAt
SongLyrics ← 歌詞分段存放
├── Id
├── SongId
├── SectionType (Verse | Chorus | Bridge | PreChorus | Outro | Intro)
├── SectionOrder (int, 排序)
├── LyricEn
└── LyricZh
SongMedia ← 多媒體連結
├── Id
├── SongId
├── MediaType (YouTubeReference | ChurchRecording | SheetMusic | ChordChart)
├── Title
├── YouTubeVideoId (只存 IDe.g. "dQw4w9WgXcQ")
├── BlobPath (Azure Blob,教會自錄影片/樂譜 PDF)
└── UploadedAt
```
**功能清單**
- 新增 / 編輯 / 封存詩歌
- 雙語歌詞分段輸入(Verse / Chorus / Bridge…)
- 搜尋:標題、標籤、CCLI 號碼、語言篩選
- 上傳樂譜 PDF / 和弦表 → Azure Blob`worship/sheets/`
- 關聯 YouTube 參考影片(只存 Video ID,嵌入播放,不下載)
- 上傳教會自錄影片 → Azure Blob(`worship/recordings/`
- 匯出歌詞 PDF(用於投影或印刷)
---
#### 3.12b 敬拜規劃 / 歌單 (Worship Set / Setlist)
**概念:** 每次主日或特別聚會,敬拜負責人規劃當次要唱的詩歌清單及順序。
```
WorshipSet
├── Id
├── ServiceDate
├── ServiceType (主日崇拜 | 禱告會 | 特別聚會)
├── LeadWorshiperId (敬拜帶領人 → Member)
├── Status (Draft | Confirmed | Completed)
├── Notes
├── CreatedByUserId
└── SetItems[]
WorshipSetItem
├── Id
├── WorshipSetId
├── SongId
├── ItemOrder (歌曲順序)
├── PlayKey (本次演唱調性,可與 Song.DefaultKey 不同)
├── Notes (e.g. "唱兩遍,不含橋段")
└── IsSkipped (bool, 臨時略過)
```
**功能清單**
- 建立歌單,從歌曲庫拖曳/搜尋新增詩歌
- 調整歌曲順序(拖曳排序)
- 指定每首歌本次演唱調性
- 歌單狀態:草稿 → 確認 → 完成
- 一鍵產生 **歌單 PDF**(供投影、印刷或發給敬拜隊)
- 歌單 PDF 包含:歌名(雙語)、歌詞、本次調性、備注
- **歷史歌單統計:** 哪些詩歌最常使用(供 AI 整合預留)
---
#### 3.12c YouTube 連結策略
> **重要:直接下載 YouTube 影片違反 YouTube 服務條款(ToS),無論用途。**
| 做法 | 合法性 | 建議 |
|------|--------|------|
| 嵌入 YouTube Player`<iframe>` | ✅ 合法 | **系統使用此方式** |
| 存 YouTube Video ID 在 DB | ✅ 合法 | 前端動態組成嵌入連結 |
| 使用 yt-dlp 或其他工具下載 | ❌ 違反 ToS | **不實作** |
| 教會自錄影片上傳 Azure Blob | ✅ 合法 | **支援此方式** |
| 從 CCLI StreamingPlus 購買下載 | ✅ 合法 | 教會可另外購買版權 |
**YouTube 嵌入實作**
```typescript
// Angular 元件中安全嵌入 YouTube
import { DomSanitizer } from '@angular/platform-browser';
get safeYouTubeUrl() {
const videoId = this.song.youTubeVideoId;
return this.sanitizer.bypassSecurityTrustResourceUrl(
`https://www.youtube.com/embed/${videoId}`
);
}
```
---
#### 3.12d CCLI 版權合規
**什麼是 CCLI**
- Church Copyright Licensing International — 教會詩歌版權授權機構
- ROLAC 應有(或應申請)CCLI License,才能合法在聚會中投影/印刷/錄製詩歌
- 每首有 CCLI 授權的詩歌都有唯一的 **CCLI Number**
**系統支援**
- 每首歌可記錄 `CcliNumber`(選填)
- 教會 CCLI License 號碼存於系統設定(`Settings.CcliLicenseNumber`
- 歌單 PDF 頁腳自動加印:`CCLI License # XXXXXXXX`
- 未來可加:使用頻率報告(CCLI 要求每年報告使用歌曲,以計算版稅)
---
#### 3.12e Azure Blob 存儲結構(敬拜模組)
```
rolac-storage/
└── worship/
├── sheets/ ← 樂譜 PDF、和弦表
│ └── {songId}-{filename}.pdf
├── recordings/ ← 教會自錄影片(MP4)
│ └── {songId}-{date}.mp4
└── setlists/ ← 歌單 PDF 匯出存檔
└── {setId}-{date}.pdf
```
**存取控制**
- `worship/sheets/` → PrivateAPI 產生 SAS URL1 小時有效)
- `worship/recordings/` → PrivateAPI 產生 SAS URL
- `worship/setlists/` → Private,敬拜負責人可下載
---
#### 3.12f 權限控管
| 操作 | 可執行角色 |
|------|-----------|
| 查看歌曲庫 | 全體登入用戶 |
| 新增 / 編輯詩歌 | `worship_leader``ministry_leader`(敬拜)、`super_admin` |
| 刪除 / 封存詩歌 | `ministry_leader`(敬拜)、`super_admin` |
| 建立 / 編輯歌單 | `worship_leader``ministry_leader`(敬拜) |
| 查看歌單 | 全體登入用戶(含一般教友查看已確認歌單) |
| 上傳影片 / 樂譜 | `worship_leader``ministry_leader` |
> 新增角色 **`worship_leader`**(敬拜帶領人)加入 RBAC 角色清單。
---
### 3.13 代禱事項 (Prayer Requests)
**核心設計原則**
- 教友可提交代禱事項,設定可見範圍
- 牧者/組長可追蹤禱告進度、標記應允
- 不強制公開,隱私保護優先
**資料模型**
```
PrayerRequest
├── Id
├── SubmittedByMemberId (提交者;可匿名提交)
├── IsAnonymous (bool — 不顯示提交者姓名)
├── Title (簡短標題)
├── Content (詳細內容)
├── Visibility (Private | CellGroup | AllLeaders | Public)
│ ← Private: 僅牧者/提交者本人
│ ← CellGroup: 提交者的小組成員
│ ← AllLeaders: 所有 Leader 及以上
│ ← Public: 所有登入教友
├── Status (Active | Answered | Archived)
├── AnsweredNote (應允見證,選填)
├── ExpiresAt (自動封存日期,預設 90 天後)
├── CellGroupId (若 Visibility = CellGroup)
├── CreatedAt
└── UpdatedAt
PrayerFollow ← 誰在為這個代禱追蹤
├── Id
├── PrayerRequestId
├── MemberId
├── PrayedAt (最後禱告時間)
└── Note (私人筆記,只有自己看到)
```
**Visibility 存取規則**
| Visibility | 可見範圍 |
|-----------|---------|
| `Private` | 提交者本人 + pastor + super_admin |
| `CellGroup` | 同小組成員 + cell_leader + district_leader + pastor |
| `AllLeaders` | ministry_leader / district_leader / coworker_chair / board_member / pastor 及以上 |
| `Public` | 所有登入教友(含 member) |
**功能清單**
- 教友提交代禱(可匿名)
- 選擇可見範圍
- 「我在為此禱告」按鈕(記錄 PrayerFollow
- 牧者 / 組長標記為「已應允」並填寫見證
- 90 天後自動封存(可延長)
- 通知:新公開代禱事項 → 推播通知給相關成員(可關閉)
- App 端「代禱牆」:顯示 Public + CellGroup 範圍的事項
**與 CMS 整合(選用)**
- 可在網站公開「代禱牆」頁面顯示 `Visibility = Public` 的事項
- 需教友登入才能查看(不對訪客公開)
**權限矩陣補充**
| 操作 | 角色 |
|------|------|
| 提交代禱 | 所有登入用戶(含 member) |
| 查看 Private | 提交者本人、pastor、super_admin |
| 標記已應允 | pastor、cell_leader(限自己小組)、super_admin |
| 刪除代禱 | 提交者本人、pastor、super_admin |
| 查看所有代禱 | pastor、super_admin |
---
### 3.11 未來 AI 整合
> 這些功能在 Phase 1/2 不實作,但資料模型與 API 設計需為其預留擴展點。
**預想功能**
| 功能 | 說明 |
|------|------|
| 關懷提醒 | 分析出席模式,自動提醒牧者關懷長時間缺席的教友 |
| 智能排班建議 | 根據服事歷史與可用性,推薦最佳排班組合 |
| 奉獻洞察 | 自然語言查詢(「今年三月奉獻比去年增加多少?」) |
| 講道摘要 | 自動摘要上傳的講道錄音 / 文稿 |
| 新訪客跟進 | AI 起草個人化歡迎信件草稿供牧者審閱後發送 |
| 聊天機器人 | 教會網站 FAQ 機器人,回答主日時間、聯絡方式等 |
**技術預留**
- 保持 API first 設計,AI 功能透過內部 API 存取資料
- 重要事件發布到事件匯流排(event bus),供 AI Agent 訂閱
- 考慮使用 Claude API (Anthropic) 作為 LLM 後端
---
## 4. 角色與權限矩陣
> ✅ 全權限  🔒 僅限自身 Scope  👁 唯讀  ❌ 無存取
>
> ROLAC 為靈糧堂體制,角色依序:牧師 > 理事 ≈ 同工會主席 > 事工領袖 > 區長 ≈ 小組長 > 同工 > 會友
| 資源 / 操作 | super_admin | pastor | board_member 理事 | coworker_chair 同工會主席 | ministry_leader 事工領袖 | district_leader 區長 | cell_leader 小組長 | coworker 同工 | finance 財務 | secretary 秘書 | member 會友 |
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
| **教友資料 — 查看** | ✅ | ✅ | ✅ | ✅ | 🔒事工 | 🔒區 | 🔒組 | 🔒事工 | 👁 | ✅ | 👁自己 |
| **教友資料 — 編輯** | ✅ | ✅ | ❌ | ❌ | 🔒事工 | ❌ | ❌ | ❌ | ❌ | ✅ | 👁自己 |
| **教友資料 — 刪除** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **小組 — 管理** | ✅ | ✅ | 👁 | ✅ | 🔒事工 | 🔒區 | 🔒組 | ❌ | ❌ | 👁 | ❌ |
| **服事表 — 查看** | ✅ | ✅ | 👁 | ✅ | 🔒事工 | 🔒區 | 🔒組 | 🔒事工 | ❌ | ✅ | 👁 |
| **服事表 — 編輯** | ✅ | ✅ | ❌ | ✅ | 🔒事工 | ❌ | 🔒組 | ❌ | ❌ | ✅ | ❌ |
| **歌曲庫 — 查看** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | 👁 |
| **歌曲庫 — 編輯** | ✅ | ✅ | ❌ | ✅ | 🔒敬拜 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **歌單 — 建立/編輯** | ✅ | ✅ | ❌ | ✅ | 🔒敬拜 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **代禱 — 提交** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| **代禱 — 查看全部** | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **代禱 — 標記應允** | ✅ | ✅ | ❌ | ✅ | ❌ | 🔒區 | 🔒組 | ❌ | ❌ | ❌ | ❌ |
| **奉獻 — 查看** | ✅ | ✅ | 👁摘要 | 👁摘要 | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | 👁自己 |
| **奉獻 — 編輯** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| **支出 — 查看** | ✅ | ✅ | 👁摘要 | 👁摘要 | 🔒事工 | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| **支出 — 申請報銷** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ |
| **支出 — 審核** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| **年度收據 — 產生** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| **月結對帳** | ✅ | 👁 | 👁 | 👁 | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ❌ |
| **CMS — 編輯** | ✅ | 👁 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ |
| **報表** | ✅ | ✅ | 👁 | 🔒 | 🔒事工 | 🔒區 | ❌ | ❌ | 🔒財務 | 👁 | ❌ |
| **Audit Log** | ✅ | 👁 | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **角色管理** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
| **系統設定** | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ |
---
## 5. 資料模型草圖
> **詳細 Schema 設計請參閱 [`docs/DB_SCHEMA.md`](./DB_SCHEMA.md)**v1.0,含完整欄位定義、索引、EF Core 設定)
```
Phase 1 核心實體關係:
┌─────────────────┐ ┌─────────────────┐
│ AppUser │────▶│ AppRole │
│ (ASP.NET │ │ (RBAC, 10 角色)│
│ Identity) │ └─────────────────┘
└────────┬────────┘
│ 0..1 : 1
┌────────▼────────┐ ┌─────────────────┐
│ Member │────▶│ FamilyUnit │
│ (教友資料) │ │ (家庭單元) │
└────┬────────┬───┘ └─────────────────┘
│ M:N │ 1:N
│ │
┌────▼──────┐ └──────────────┐
│ Ministry │ ┌───▼──────────────┐
│ (10 部門) │ │ MemberMinistry │
└────┬──────┘ │ (服事歸屬) │
│ 1:N └──────────────────┘
┌────▼──────────────┐
│ UserMinistry │
│ (Ministry Scope) │
└───────────────────┘
┌──────────────────┐ ┌─────────────────────┐
│ OfferingSession │───▶│ Giving │
│ (主日奉獻批次) │ │ (奉獻記錄) │
└──────────────────┘ └──────────┬──────────┘
│ 1:N
┌─────────────────┐
│ GivingReceipt │
│ (年度收據) │
└─────────────────┘
┌──────────────────────┐ ┌─────────────────────┐
│ ExpenseCategoryGroup │────▶│ ExpenseSubCategory │
│ (大類: 設備/餐飲…) │ │ (子項目) │
└──────────────────────┘ └──────────┬──────────┘
┌──────────────────────────────────────▼─────────────┐
│ Expense │
│ (MinistryId + CategoryGroupId + SubCategoryId) │
│ Type: VendorPayment | StaffReimbursement │
└──────────────────────────────────────┬─────────────┘
│ 1:N
┌────────────▼────────────┐
│ MonthlyStatement │
│ (月底對帳表) │
└─────────────────────────┘
┌───────────────┐ ┌──────────────┐
│ PrayerRequest │────▶│ PrayerFollow │
│ (代禱事項) │ │ (跟進記錄) │
└───────────────┘ └──────────────┘
AuditLogbigint PKimmutable,所有操作均記錄)
```
**各模組 Table 數量:**
| 模組 | Tables | Phase |
|------|--------|-------|
| Identity & Auth | 8ASP.NET Identity 6 + UserMinistry + UserDevice| P1 |
| Member Management | 4Member, FamilyUnit, MemberMinistry, MemberTag| P1 |
| Ministry | 1 | P1 |
| CMS | 4CmsPage, Announcement, SermonVideo, ContactInquiry| P1 |
| Giving | 5GivingCategory, OfferingSession, Giving, GivingReceipt, GivingRecurringSchedule| P1 |
| Expense | 4ExpenseCategoryGroup, ExpenseSubCategory, Expense, MonthlyStatement| P1 |
| Prayer Requests | 2 | P1 |
| Audit Log | 1 | P1 |
| Notifications | 1 | P1 |
| **Phase 1 Total** | **30 Tables** | |
| Service Roster | 2ServiceSlot, ServiceAssignment| P2 |
| Sunday Attendance | 1 | P2 |
| Cell Groups | 4CellGroup, CellGroupMembership, CellGroupMeeting, CellGroupAttendance| P2 |
| Ministry Budget | 1 | P3 |
| **Grand Total** | **38 Tables** | |
---
## 6. 技術架構 (已確定)
### 技術選型
| 層 | 技術 | 說明 |
|----|------|------|
| 前端 / App | **Angular + Capacitor** | Web → iOS & Android 打包 |
| Admin/Portal UI | **Angular Material + Kendo UI** | Dashboard、資料表格、排班 |
| Landing Page UI | **Tailwind CSS + 自訂元件** | 設計感公開網站 |
| 後端 API | **ASP.NET Core (C#)** | REST API |
| ORM | **Entity Framework Core** | Code-first migrations |
| 資料庫 | **PostgreSQL** | 自架於 Docker |
| 檔案儲存 | **本地檔案儲存(現階段)→ Azure Blob Storage(未來)** | 透過 `IFileOperationService` 抽象,現以 `LocalFileOperationService`base folder 由 config 設定)實作,未來切換 `AzureFileOperationService`。教友照片、PDF 收據、CMS 媒體 |
| 原始碼管理 | **Gitea** | 自架,Docker 容器 |
| CI/CD | **Jenkins** | 自架,Docker 容器 |
| 容器化 | **Docker + Docker Compose** | 所有服務容器化 |
| 主機 | **Azure VM (Ubuntu)** | Microsoft Nonprofit $2,000 credit |
| 反向代理 | **Nginx** | SSL 終止、路由分發 |
| SSL | **Let's Encrypt + Certbot** | 免費自動續期 |
| Email | **SendGrid****SMTP Relay** | 通知、收據寄送 |
| SMS | **Twilio** | 服事提醒、緊急通知 |
| PDF 產生 | **QuestPDF** (C# library) | 年度收據、報表 |
| 多語言 | **ngx-translate** | 執行期語言切換(en / zh-TW |
### Mobile App 策略 (Angular + Capacitor)
**為什麼用 Capacitor 而非純 PWA**
| 需求 | PWA | Capacitor |
|------|-----|-----------|
| App Store / Play Store 上架 | ❌ | ✅ |
| 推播通知 (Push Notification) | 有限 | ✅ 原生 |
| 相機(教友照片) | 有限 | ✅ 原生 |
| 離線快取 | Service Worker | ✅ 原生 |
| 開發成本 | 低 | 中(同一 Angular codebase |
**Capacitor 整合要點**
- 同一份 Angular codebase → Web + iOS App + Android App
- 使用 `@capacitor/push-notifications` 推播服事提醒
- 使用 `@capacitor/camera` 拍攝/上傳教友照片
- 使用 `@capacitor/local-notifications` 本機提醒(排班、生日)
- Jenkins CI 額外加 `npx cap build ios` / `npx cap build android` 步驟
**Mobile-First 設計規範**
| 規範 | 要求 |
|------|------|
| 觸控目標 | 最小 44 × 44 px |
| 字體基礎 | 16px(不可縮放到 < 12px |
| 底部導覽列 | 主要功能用 Bottom Tab Bar,不用側邊 Sidebar |
| 表單 | 單欄式,每個 input 標明 inputmode(數字、Email…) |
| 圖表 | 可橫向捲動,不縮小到難以閱讀 |
| 圖片 | 懶加載(lazy loading)、WebP 格式優先 |
| 離線提示 | 網路斷線時顯示快取資料 + 明確提示 |
### 多語言 (i18n) 策略
**使用 ngx-translate(非 Angular 內建 i18n**
- 理由:執行期動態切換,不需要分開 build;適合 App 場景
- 語言包:`assets/i18n/en.json``assets/i18n/zh-TW.json`
- 語言偏好存於 `localStorage` / Capacitor Preferences
- API 回傳資料中,多語系欄位用平行欄位設計(見下)
**雙語適用範圍(全系統)**
| 範疇 | 雙語方式 |
|------|----------|
| Admin App UI 標籤 | ngx-translate JSON`en.json` / `zh-TW.json` |
| 公開網站 UI 標籤 | 同上 |
| CMS 內容(公告、活動、頁面)| DB 平行欄位(`title_en` / `title_zh` |
| Ministry / 服事類型名稱 | DB 平行欄位 |
| Email / SMS 通知內容 | 依教友語言偏好選範本 |
| IRS 年度收據 | 英文為主,繁中附於後 |
| PDF 報表 | 後台報表以 UI 語言為準 |
**多語系資料欄位命名慣例**
```
// DB 欄位(需要雙語的 Entity
name_en / name_zh
title_en / title_zh
description_en / description_zh
body_en / body_zh
// ngx-translate keyUI 固定字串)
assets/i18n/en.json
assets/i18n/zh-TW.json
```
**語言切換 UI**
- 公開網站 & Admin App Header 右上角 `EN | 中` 切換按鈕
- 切換後立即套用,不重整頁面
- 教友個人設定可儲存語言偏好(`Member.LanguagePreference`
- 公開網站 URL 加 `/en/``/zh/` 前綴,對 SEO 最友善
### 其他核心架構原則
- **JWT + Refresh Token** — API 無狀態認證
- **ASP.NET Core Identity** — 使用者管理、密碼雜湊
- **全新資料庫** — 無舊資料遷移需求,直接 EF Code-First Migration
### 檔案儲存抽象層 (File Storage Abstraction)
> **背景(2026-05-29 決定):** Microsoft 拒絕了我們的 Nonprofit / Azure 申請,**現階段無法使用 Azure Blob Storage**。
> 因此先改用**本地檔案儲存**base folder 由 config 設定),未來取得 Azure 額度後再切換為 Azure Blob,**業務程式碼不需改動**。
**設計:以介面抽象隔離儲存後端**
所有檔案讀寫(教友照片、PDF 收據、CMS 媒體、收據照片、敬拜樂譜/影片等)一律透過 `IFileOperationService` 介面操作,業務層不直接依賴任何特定儲存實作。
```
IFileOperationService ← 抽象介面(業務層僅依賴此介面)
├── SaveAsync(path, stream) → 儲存檔案,回傳相對路徑 / 識別碼
├── GetAsync(path) → 讀取檔案串流
├── DeleteAsync(path) → 刪除檔案
├── ExistsAsync(path) → 是否存在
└── GetUrlAsync(path, expiry?) → 取得存取 URL(本地:API 代理路徑;AzureSAS URL
LocalFileOperationService ← 現階段實作(✅ Phase 1)
├── Base folder 從 config 讀取(e.g. FileStorage:BasePath
├── 路徑沿用既有結構(cms/images/、finance/receipts/、worship/… 相對於 base folder
├── Private 檔案經由 API 代理 + 角色授權提供存取(取代 SAS URL)
└── 容器化時 base folder 對映到 Docker volume,確保持久化
AzureFileOperationService ← 未來實作(🔜 取得 Azure 額度後)
├── 對映到 Azure Blob Container
├── GetUrlAsync 回傳具時效的 SAS URL
└── 切換方式:DI 註冊改綁此實作 + 一次性資料搬移,業務程式碼零改動
```
**設定範例(appsettings**
```jsonc
"FileStorage": {
"Provider": "Local", // Local | Azure(未來)
"BasePath": "/data/rolac-files" // 本地 base folder(容器內對映 volume
}
```
> **與本文件其他段落的關係:** 文中所有提到 `Azure Blob`blob 路徑的地方(§3.5 CMS 圖片、§3.6d `finance/receipts/`、§3.12e `worship/...`),現階段一律改由 `IFileOperationService` + `LocalFileOperationService` 以對應相對路徑儲存於 config 的 base folder 下;待 Azure 開通後切換 `AzureFileOperationService` 即還原為 Blob 儲存,路徑結構不變。
---
## 7. 開發階段規劃 (Roadmap)
### Phase 0 — 技術基礎 & DevOps (23 週)
- [X] Azure VM 建置 + Docker 環境
- [ ] Gitea + Jenkins 啟動,CI pipeline 通
- [ ] Angular + Capacitor 專案骨架(含 ngx-translate en/zh-TW
- [X] ASP.NET Core API 骨架 + EF Core 初始 Migration
- [X] 認證系統(JWT + Refresh Token + ASP.NET Identity
- [X] RBAC 框架(角色 + Ministry Scope middleware
- [ ] Audit Log 基礎建設
- [ ] 檔案儲存抽象層(`IFileOperationService` + `LocalFileOperationService`base folder 從 config 讀取)
- [ ] Mobile-first UI 元件庫設定(底部導覽、touch target 規範)
### Phase 1 — 六月上線 MVP5 週,目標 2026-06-30
#### 教會首頁 CMSrolac.org
- [ ] Landing Page 基礎設計(Tailwind,簡化版先上線)
- [ ] 首頁 Hero / 關於我們 / 主日資訊(雙語)
- [ ] 消息公告(雙語,支援排程發佈)
- [ ] 講道影片嵌入(YouTube
- [ ] 聯絡表單
#### 教友管理
- [ ] 教友 CRUD(基本資料、照片上傳,經 `IFileOperationService` → 本地儲存)
- [ ] 家庭單元管理
- [ ] 搜尋 / 篩選(姓名、狀態、小組)
- [ ] 教友狀態(會員 / 訪客 / 前會員)
- [ ] i18n 語言切換 UIEN / 中 按鈕)
#### 奉獻追蹤(手動)
- [X] 奉獻類型設定(Tithe / Offering / Special
- [X] 單筆奉獻記錄(現金 / 支票 / Zelle / PayPal
- [X] **主日奉獻袋批次輸入(OfferingSession** ← 鍵盤優先快速錄入
- [ ] 個人奉獻歷史查詢(教友 App 端)
- [ ] 年度收據 PDF 產生(QuestPDFEIN 42-2682968
- [ ] 年度收據 Email 批次寄送
#### 支出追蹤 & 報銷
- [ ] 支出類別設定(11 大類種子:設備 / 餐飲 / 人事 / 宣教…)
- [ ] 廠商直接付款記錄(含支票號碼)
- [ ] 同工代墊報銷申請(含收據照片上傳)
- [ ] 財務審核流程(Pending → Approved → Paid
- [ ] 月結對帳表(期初餘額 + 奉獻 − 支出 = 帳面期末,對比銀行對帳單)
#### 代禱事項(Prayer Requests
- [ ] 教友提交代禱請求(含 4 個可見範圍:Private / CellGroup / AllLeaders / Public
- [ ] 代禱清單瀏覽(依角色過濾可見範圍)
- [ ] 牧者標記「已應允」(PrayerFollow 記錄)
- [ ] 代禱事項到期提醒(可選,Email)
#### 基礎功能
- [ ] 登入 / 登出 / 密碼重設
- [ ] RBAC 角色指派(super_admin、finance、secretary、member 先上)
- [ ] Email 通知(SendGrid,收據寄送用)
- [ ] Capacitor 打包(Android APK 供內部測試)
### Phase 2 — 服事 & 小組(2026 年 810 月,目標 10 週)
> **已確認範圍**2026-05-24 決定)
#### 服事表排班(Service Roster
- [ ] 服事項目設定(雙語名稱,對應 10 個事工部門)
- [ ] Kendo Scheduler 排班視圖(月/週/日)
- [ ] 衝突檢查(同一人同日多項服事)
- [ ] 排班通知(Email + Push,服事前 3 天)
- [ ] 缺席申請 & 替換流程
- [ ] 服事表 PDF 匯出(週服事表)
#### 主日出席統計(Sunday Attendance
- [ ] 每週輸入:大人 / 青少年 / 兒童 三欄
- [ ] 歷史趨勢圖(Kendo Chart,年度/季度)
- [ ] 報表匯出(CSV
#### 小組管理(Cell Group Management
- [ ] 小組架構建立(巢狀樹狀,支援多層)
- [ ] 組長指派(組長擁有自身小組資料權限)
- [ ] 小組成員指派 / 移轉
- [ ] 小組聚會記錄(CellGroupMeeting + 出席 Attendance
- [ ] 小組出席報表
---
### Phase 3 — 財務深化(2026 年 11 月 → 2027 年,目標 4–6 週)
> **已確認範圍**2026-05-24 決定)
#### 事工預算(Ministry Budget
- [ ] 各事工年度預算設定(MinistryBudgetMinistry + CategoryGroup + SubCategory + FiscalYear + BudgetAmount
- [ ] 預算 vs 實際支出對比(Kendo Chart
- [ ] 超支警示(支出超過預算 80% 時通知財務)
- [ ] 年度預算報表 PDF 匯出
> **備注:** Category 結構已於 Phase 1 設計完成,無需 Schema 異動,直接新增 MinistryBudget 表即可。
---
### Phase 4 — 線上奉獻(2027 年,目標 4–6 週)
> **已確認**Stripe(主)+ PayPal Checkout(次)
- [ ] 線上奉獻頁(rolac.org/give)— Stripe 整合
- [ ] Stripe Webhook → 自動建立 Giving 記錄
- [ ] 定期奉獻(Stripe Subscription
- [ ] PayPal Checkout 按鈕(次要選項)
- [ ] 申請 Stripe Nonprofit 費率(目標 1.5% + $0.30
---
### 暫緩模組(無明確時程)
| 模組 | 原因 |
|------|------|
| 敬拜歌曲庫(Worship Song Library | CCLI License 未確認;優先級 TBD |
| 事工報表(Reports Dashboard | 等 Phase 12 資料累積後再做 |
| 廣播通知(Broadcast Messaging | 低優先,Email 手動寄送可暫代 |
| 會員目錄 / 通訊錄 | 隱私設計複雜,暫緩 |
| 兒童特殊欄位 | 緊急聯絡人 / 過敏 / 接送授權,暫緩 |
---
### Phase 5 — App 上架 & AI 整合(持續)
- [ ] App Store 正式上架(需 Apple Developer 帳號 $99/年)
- [ ] Google Play 上架($25 一次性)
- [ ] 效能優化 & 安全稽核
- [ ] 出席缺席關懷 AI 提醒(Claude API / Anthropic
- [ ] 智能排班建議
- [ ] 自然語言報表查詢
- [ ] 代禱事項 AI 摘要(週代禱精華)
---
## 8. 開放議題 & 待決策
| # | 議題 | 選項 / 說明 | 優先級 | 狀態 |
|---|------|-------------|--------|------|
| 1 | ~~技術選型確認~~ | **已決定**: C# API + EF + PostgreSQL + Angular | 🔴 高 | ✅ 決定 |
| 2 | ~~部署環境~~ | **已決定**: Azure VM (Ubuntu + Docker)Nonprofit Credit | 🟡 中 | ✅ 決定 |
| 3 | ~~主日出席如何記錄?~~ | **已決定**: 招待同工統計,分大人/青少年/兒童,秘書手動輸入 | 🔴 高 | ✅ 決定 |
| 4 | ~~奉獻支付方式~~ | **已決定**: 現金+支票(現有),Zelle+PayPal(即將開放),手動輸入為主,PayPal 未來可 Webhook | 🔴 高 | ✅ 決定 |
| 5 | ~~域名~~ | **已確認**: rolac.org | 🔴 高 | ✅ 決定 |
| 6 | ~~多語言範圍~~ | **已決定**: 英文 + 繁體中文,不需簡中 | 🟡 中 | ✅ 決定 |
| 7 | ~~手機 App~~ | **已決定**: Angular + Capacitor 打包 iOS/Android | 🟡 中 | ✅ 決定 |
| 8 | ~~資料遷移~~ | **已決定**: 無現有資料,全新建置 | 🟡 中 | ✅ 決定 |
| 9 | ~~Staging 環境~~ | **已決定**: Phase 2 再建,同 VM port 分離 | 🟡 中 | ✅ 決定 |
| 10 | AI 整合時程 | Phase 5,但 API 設計預留擴展點 | 🟢 低 | ⏳ 待決 |
| 11 | ~~收據 IRS EIN~~ | **已確認**: EIN `42-2682968`,存為環境變數,不 hardcode | 🟡 中 | ✅ 決定 |
| 12 | ~~SMS 通知~~ | **已決定**: 使用 Twilio | 🟢 低 | ✅ 決定 |
| 13 | App Store 上架策略 | Phase 12 TestFlight 內部測試;Phase 5 正式上架 | 🟡 中 | ⏳ Phase 5 再決 |
| 14 | ~~代禱事項~~ | **已決定**: 納入 Phase 1,4 個可見範圍 | 🔴 高 | ✅ 決定 |
| 15 | ~~線上奉獻整合~~ | **已決定**: Phase 4Stripe(主)+ PayPal Checkout(次),Nonprofit 費率申請 | 🔴 高 | ✅ 決定 |
| 16 | 會員目錄 / 通訊錄 | 隱私考量需 Opt-in,暫緩 | 🟢 低 | ⏳ 未來再評估 |
| 17 | 兒童特殊欄位 | 緊急聯絡人、過敏、接送授權,暫緩 | 🟢 低 | ⏳ 未來再評估 |
| 18 | CCLI License 號碼 | 教會是否已有 CCLI License?需填入系統設定 | 🟡 中 | ⏳ 待確認 |
| 19 | ~~Phase 2/3 模組分配~~ | **已決定**Phase 2 = 服事表排班 + 主日出席統計 + 小組管理;Phase 3 = 事工預算 | 🔴 高 | ✅ 決定 |
| 20 | ~~檔案儲存後端~~ | **已決定**:Microsoft 拒絕申請,暫無法用 Azure Blob。改以 `IFileOperationService` 抽象,現階段用 `LocalFileOperationService`base folder 由 config 設定),未來取得 Azure 額度後切換 `AzureFileOperationService`(見 §6 檔案儲存抽象層) | 🔴 高 | ✅ 決定 |
---
## 下拉雙語顯示約定 (Dropdown Bilingual Display)
所有業務下拉選單的選項同時顯示英文與中文,格式 `英文/中文`(無空格、英文在前;無中文則只顯示英文)。
- **DB 查表下拉**giving categories / ministries / expense category groups & subs):
在對應的 API service 載入時計算 `label = bilingual(name_en, name_zh)`,模板綁 `textField="label"`
不改 DB、不改 schema、不改儲存值。
- **寫死 enum 下拉**payment method / expense status / member status / gender / language / roles):
選項一律定義在 `APP/src/app/shared/i18n/option-lists.ts`,型別 `{ value, label }`
模板綁 `textField="label" valueField="value" [valuePrimitive]="true"`。送出/儲存的 `value` 不變。
- 共用工具:`APP/src/app/shared/i18n/bilingual.ts``bilingual(en, zh)`
- 新增下拉時沿用以上兩種模式,不要在 component 內各自寫死中文字串。
*文件由 ROLAC 開發團隊維護。*