# ROLAC Church Management System — Project Planning

**教會:** River Of Life Christian Church In Arcadia (ROLAC)  
**規模:** 50–100 人  
**文件版本:** 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 | 2–5 | 每週 |
| 敬拜 | 司琴 Pianist | 1 | 每週 |
| PPT/影音 | PPT 操作 Slide Operator | 1 | 每週 |
| PPT/影音 | 攝影 / 直播 Livestream | 0–1 | 每週 |
| 音控 | 音控 Sound Engineer | 1 | 每週 |
| 場地組 | 場地佈置 Setup | 2–3 | 每週 |
| 場地組 | 場地收拾 Teardown | 2–3 | 每週 |
| 招待 | 門口招待 Greeter | 2–3 | 每週 |
| 招待 | 奉獻袋 Offering Collection | 2 | 每週 |
| 兒牧 | 兒童主日學老師 | 1–2 | 每週 |
| 兒牧 | 兒童助教 Assistant | 0–1 | 每週 |
| 餐飲 | 愛宴負責人 Catering Lead | 1 | 每週 |
| 餐飲 | 愛宴協助 Catering Help | 2–4 | 每週 |

**功能清單**
- 定義服事槽位（綁定 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 編輯器 UI（Admin 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 Subscription，Phase 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 拍攝，即時上傳
- 或桌機版直接上傳圖片檔
- Private，API 產生 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   (只存 ID，e.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/` → Private，API 產生 SAS URL（1 小時有效）
- `worship/recordings/` → Private，API 產生 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 │
│ (代禱事項)    │     │ (跟進記錄)   │
└───────────────┘     └──────────────┘

AuditLog（bigint PK，immutable，所有操作均記錄）
```

**各模組 Table 數量：**

| 模組 | Tables | Phase |
|------|--------|-------|
| Identity & Auth | 8（ASP.NET Identity 6 + UserMinistry + UserDevice）| P1 |
| Member Management | 4（Member, FamilyUnit, MemberMinistry, MemberTag）| P1 |
| Ministry | 1 | P1 |
| CMS | 4（CmsPage, Announcement, SermonVideo, ContactInquiry）| P1 |
| Giving | 5（GivingCategory, OfferingSession, Giving, GivingReceipt, GivingRecurringSchedule）| P1 |
| Expense | 4（ExpenseCategoryGroup, ExpenseSubCategory, Expense, MonthlyStatement）| P1 |
| Prayer Requests | 2 | P1 |
| Audit Log | 1 | P1 |
| Notifications | 1 | P1 |
| **Phase 1 Total** | **30 Tables** | |
| Service Roster | 2（ServiceSlot, ServiceAssignment）| P2 |
| Sunday Attendance | 1 | P2 |
| Cell Groups | 4（CellGroup, 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 key（UI 固定字串）
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 代理路徑；Azure：SAS 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 (2–3 週)
- [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 — 六月上線 MVP（5 週，目標 2026-06-30）

#### 教會首頁 CMS（rolac.org）
- [ ] Landing Page 基礎設計（Tailwind，簡化版先上線）
- [ ] 首頁 Hero / 關於我們 / 主日資訊（雙語）
- [ ] 消息公告（雙語，支援排程發佈）
- [ ] 講道影片嵌入（YouTube）
- [ ] 聯絡表單

#### 教友管理
- [ ] 教友 CRUD（基本資料、照片上傳，經 `IFileOperationService` → 本地儲存）
- [ ] 家庭單元管理
- [ ] 搜尋 / 篩選（姓名、狀態、小組）
- [ ] 教友狀態（會員 / 訪客 / 前會員）
- [ ] i18n 語言切換 UI（EN / 中 按鈕）

#### 奉獻追蹤（手動）
- [X] 奉獻類型設定（Tithe / Offering / Special）
- [X] 單筆奉獻記錄（現金 / 支票 / Zelle / PayPal）
- [X] **主日奉獻袋批次輸入（OfferingSession）** ← 鍵盤優先快速錄入
- [ ] 個人奉獻歷史查詢（教友 App 端）
- [ ] 年度收據 PDF 產生（QuestPDF，EIN 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 年 8–10 月，目標 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）
- [ ] 各事工年度預算設定（MinistryBudget：Ministry + 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 1–2 資料累積後再做 |
| 廣播通知（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 1–2 TestFlight 內部測試；Phase 5 正式上架 | 🟡 中 | ⏳ Phase 5 再決 |
| 14 | ~~代禱事項~~ | **已決定**: 納入 Phase 1，4 個可見範圍 | 🔴 高 | ✅ 決定 |
| 15 | ~~線上奉獻整合~~ | **已決定**: Phase 4，Stripe（主）+ 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 開發團隊維護。*
