SRS + SDS 文件 v1.0.0 2025-03-29
🏗️ 軟體設計文件套組
大樓財務雲端管理平台
Building Finance Cloud Management Platform
文件類型 SRS + SDS 完整規格書
版本 v1.0.0 初稿
撰寫日期 2025-03-29
商業模式 Multi-tenant B2B2C SaaS
Next.js 15 App Router Supabase + RLS PostgreSQL RBAC 三層權限 FIFO 沖帳演算法 Decimal 精準計算 Multi-tenant 台灣大樓管委會
📑 目錄 Table of Contents
S1

專案背景與目標

Project Background & Objectives

1.1 問題陳述
😤
痛點一:對帳效率低落

管委會每月需人工核對每戶繳費紀錄,容易出現遺漏或錯誤,爭議處理耗時費力。

💸
痛點二:預繳/欠費難追蹤

住戶預繳金額無法自動沖抵帳單,欠費累積缺乏系統性催繳機制。

📊
痛點三:財報不透明

住戶無法即時查看大樓公共財務,公共費用支出缺乏透明度,引發住戶不信任。

🔐
痛點四:資料安全隱患

多棟大樓共用系統時,不同大樓的住戶財務資料可能互相可見,存在資安風險。

1.2 產品願景與目標
💡
產品願景:打造台灣大樓管委會的「數位財務大腦」——讓每一筆收支都有跡可查,讓每一個帳單都能自動平衡,讓每一位住戶都對大樓財務充滿信任。
OBJ-01
自動化對帳
實現收款與帳單的 FIFO 自動沖抵,將月結對帳工時從 8 小時縮短至 0。
OBJ-02
即時財務可視化
提供住戶即時帳單查詢與大樓財報公開平台,提升管委會公信力。
OBJ-03
多租戶安全隔離
以 RLS 技術確保各大樓資料絕對隔離,符合個資法安全要求。
OBJ-04
保全作業數位化
提供平板優化介面,讓保全人員輕鬆完成收費登記,消除紙本作業。
1.3 技術選型決策
Next.js 15
前端框架 (App Router)
Supabase
Auth + PostgreSQL + RLS
Tailwind CSS
UI 樣式框架
Shadcn UI
元件庫
NUMERIC(19,4)
高精度十進位計算
Supabase RLS
Row Level Security
S2

用戶角色定義 (RBAC)

Role-Based Access Control

👑
Committee Admin
管委會管理員
代表大樓管委會,擁有該棟大樓的最高管理權限,負責系統設定、帳號管理及財務審核。
  • 建立/停用保全帳號
  • 設定計費週期與費率
  • 批量產生帳單
  • 審核所有交易紀錄
  • 匯出完整財務報表
  • 管理公告與住戶資訊
  • 查看零用金支出稽核
🛡️
Guard
保全人員
大樓第一線人員,透過平板進行日常收費作業,所有操作均留存個人簽章以確保可追責性。
  • 搜尋住戶(姓名/戶號)
  • 登記現金/轉帳收款
  • 登記零用金支出
  • 查看自己的操作紀錄
  • 無法查看其他保全的紀錄
  • 無法修改/刪除已登記資料
  • 無法存取計費設定
🏠
Resident
住戶
大樓住戶,僅能查看個人帳務資訊與大樓公開財報,資料存取嚴格限制於本人所屬單位。
  • 查看本戶帳單明細
  • 查看本戶餘額(正/負)
  • 查看繳費紀錄
  • 查看大樓公開財報
  • 接收系統通知
  • 無法查看其他住戶資料
  • 無法查看保全操作紀錄
2.1 RBAC 權限矩陣
功能模組 操作 Committee_Admin Guard Resident
住戶管理 新增/編輯住戶
搜尋住戶 ✅(有限欄位)
查看住戶完整資料 僅本人
停用住戶帳號
帳單管理 批量產生帳單
查看所有帳單
查看本戶帳單
收款作業 登記收款(現金) ✅(需簽章)
登記收款(轉帳) ✅(需簽章)
修改收款紀錄
刪除/作廢收款
零用金 登記零用金支出 ✅(需簽章)
審核零用金
財務報表 查看完整收支報表
查看公開財報
匯出 PDF/Excel 有限匯出
系統設定 設定計費費率
管理保全帳號
S3

用戶故事 (User Stories)

以角色為中心的需求敘述,含驗收條件

3.1 管委會管理員故事
US-A01 批量產生月費帳單 P0 核心
身為 管委會管理員,我希望能夠一鍵產生本月所有住戶的管理費帳單,以便節省逐戶手動建立的時間,並確保帳單金額依照預設費率自動計算正確。
驗收條件:
  • 系統依照 billing_configs 中設定的費率自動計算每戶金額
  • 可選擇帳單週期(月/季/年)與到期日
  • 重複產生同月帳單時,系統警告並防止重複
  • 產生後住戶立即收到系統通知
  • 所有帳單初始狀態為 'pending'
US-A02 查看欠費住戶清單 P0 核心
身為 管委會管理員,我希望能夠一目了然地看到所有欠費住戶名單及欠費金額,以便進行催繳作業並掌握整體財務狀況。
  • 顯示所有 balance 為負值的住戶單位,依欠費金額由高到低排序
  • 可篩選欠費天數(逾期 30/60/90 天以上)
  • 可批量發送催繳通知
  • 欠費金額以紅色標示,預繳以綠色標示
US-A03 審核保全操作記錄 P1
身為 管委會管理員,我希望能夠查看並審核所有保全人員的收款操作紀錄,確保每筆現金都有跡可查,防止舞弊行為。
  • 可依保全姓名、日期範圍篩選操作紀錄
  • 每筆紀錄顯示經手人 ID、時間戳記、金額與住戶資訊
  • 可對有疑問的紀錄標記「待審核」旗幟
  • 支援匯出稽核報告(PDF)
US-A04 設定計費費率 P0 核心
身為 管委會管理員,我希望能夠為不同類型的住戶單位(住宅/商辦/車位)設定不同的月費費率,系統將自動套用正確費率產生帳單。
  • 可設定費率類型:固定金額或每坪單價
  • 費率變更有生效日期,不影響歷史帳單
  • 費率儲存使用 NUMERIC(19,4) 確保精準度
  • 修改費率前需二次確認
3.2 保全人員故事
US-G01 快速搜尋住戶並登記收款 P0 核心
身為 保全人員,我希望能夠用大量觸控操作快速找到住戶並登記收款,因為住戶在櫃台等待不能太久,整個流程要在 30 秒內完成。
  • 搜尋框支援姓名、門牌號、電話模糊搜尋
  • 搜尋結果即時顯示(輸入 2 字後觸發)
  • 顯示住戶當前未結清帳單與餘額
  • 一鍵選擇收款金額(可全付或自訂金額)
  • 選擇收款方式(現金/轉帳)並確認
  • 完成後自動觸發 FIFO 沖帳演算法
  • 收款紀錄自動記錄保全 ID(guard_id)
US-G02 登記零用金支出 P1
身為 保全人員,我希望能夠登記日常零用金支出(如購買清潔用品),並附上說明,讓管委會可以追蹤公共費用使用情況。
  • 填寫支出金額、類別、說明
  • 支援上傳收據照片(儲存至 Supabase Storage)
  • 提交後狀態為「待審核」,等待管委員確認
  • 每筆支出記錄操作保全 ID
3.3 住戶故事
US-R01 查看個人帳單與餘額 P0 核心
身為 住戶,我希望能夠隨時查看我家的待繳帳單、歷史繳費紀錄以及目前的餘額,確認我的繳費是否已被正確入帳。
  • 首頁顯示帳戶餘額(正數綠色=預繳,負數紅色=欠費)
  • 顯示所有帳單清單,含狀態(待繳/部分繳/已繳/逾期)
  • 可查看每張帳單的沖帳明細(哪筆付款抵了哪張帳單)
  • 顯示最近 12 個月繳費歷史
US-R02 查看大樓公開財報 P1
身為 住戶,我希望能夠查看大樓每月的收支摘要報表,了解公共費用如何使用,對管委會財務管理更有信心。
  • 顯示月度收入總計(管理費、停車費)
  • 顯示月度支出總計(依類別分類)
  • 顯示本月結餘與累計公積金餘額
  • 不顯示其他住戶個人資訊
3.4 系統自動化故事
US-S01 FIFO 自動沖帳 P0 核心
身為 系統,當保全登記一筆收款後,我需要自動依照 FIFO 原則,從最舊的未結帳單開始抵扣,更新帳單狀態與住戶餘額,所有操作必須在單一資料庫交易中完成。
  • 收款金額優先抵扣最舊(due_date 最早)的帳單
  • 單張帳單可被多次付款部分抵扣(partial payment)
  • 收款金額超過所有帳單總額時,超出部分計入 unit.balance 作為預繳
  • 所有操作在 PostgreSQL 交易(Transaction)中執行,確保原子性
  • 沖帳結果記錄於 payment_allocations 表
S4

功能需求清單

Functional Requirements (FR)

4.1 多租戶管理模組 (MT)
FR-MT-01
大樓自助註冊
管委會可自行在平台建立大樓工作空間(Building),輸入大樓名稱、地址、棟數等基本資訊後,自動成為該工作空間的第一位 Committee_Admin。
FR-MT-02
租戶隔離保證
所有資料庫查詢必須透過 RLS 策略確保,不同大樓(building_id 不同)的資料完全隔離,即便是程式碼層級的查詢也無法跨租戶存取。
FR-MT-03
方案訂閱管理
支援不同方案層級(Free / Pro / Enterprise),限制每個方案的住戶單位數上限、功能開關與資料保留期限。
4.2 帳務核心模組 (FIN)
FR-FIN-01
帳單產生引擎
支援手動與排程自動產生帳單,依 billing_configs 設定費率計算,使用 NUMERIC(19,4) 儲存,避免浮點誤差。
FR-FIN-02
FIFO 自動沖帳
收款後執行 FIFO 演算法,由最舊帳單開始抵扣,全程在 PostgreSQL 交易中執行,沖帳記錄存於 payment_allocations。
FR-FIN-03
帳戶餘額維護
unit.balance 在每次收款/帳單產生/退款時自動更新,正數=預繳,負數=欠費,不得使用浮點數計算。
FR-FIN-04
零用金管理
支援保全登記零用金支出,管委員審核後計入大樓收支報表,支援收據圖片上傳。
FR-FIN-05
退款與沖正
管委員可對已入帳的付款執行退款操作,系統自動反向更新受影響的帳單狀態與餘額。
4.3 報表模組 (RPT)
FR-RPT-01
月度收支報表
提供月度收入/支出/結餘統計,支援依類別(管理費/停車費/公共費用)分類呈現,可匯出 PDF。
FR-RPT-02
欠費清單報表
顯示所有負餘額住戶,含欠費金額、最早欠費帳單日期、聯絡資訊,支援匯出 Excel。
FR-RPT-03
保全操作稽核報表
顯示指定時間範圍內所有保全的收款操作,含金額、住戶、時間,支援按保全個人彙總。
S5

非功能需求

Non-Functional Requirements (NFR)

效能 (Performance)
  • 頁面首次載入 < 3 秒(LCP)
  • API 回應時間 < 500ms(P95)
  • FIFO 沖帳計算 < 2 秒(100 張帳單內)
  • 搜尋住戶回應 < 300ms
🔒
安全性 (Security)
  • 所有 API 必須驗證 JWT Token
  • RLS 確保資料跨租戶隔離
  • 敏感欄位傳輸使用 HTTPS/TLS 1.3
  • 密碼使用 bcrypt(cost factor ≥ 12)
  • 操作日誌不可刪除(Append-only)
📱
可用性 (Usability)
  • 支援平板裝置(10"+ 螢幕)
  • 觸控目標最小 48×48px
  • 保全核心流程 ≤ 3 個頁面
  • 支援繁體中文(主要)
  • 色彩對比度符合 WCAG 2.1 AA
🏗️
可維護性 (Maintainability)
  • 所有核心 Table 預留 metadata JSONB
  • 資料庫變更使用 Migration 管理
  • TypeScript 嚴格模式 (strict: true)
  • 核心函式測試覆蓋率 ≥ 80%
📈
可擴充性 (Scalability)
  • 單一大樓支援 1000+ 住戶單位
  • 平台支援 10,000+ 大樓租戶
  • 資料庫 Read Replica 支援報表查詢
  • 關鍵欄位建立複合索引
🔢
精準度 (Precision)
  • 所有金額欄位使用 NUMERIC(19,4)
  • 嚴禁使用 FLOAT / DOUBLE 儲存金額
  • 前端計算使用 decimal.js 函式庫
  • 小數點後 4 位確保台幣計算精準
D1

資料庫 ER Model Schema

完整 PostgreSQL 資料表定義(Supabase)

1.1 邏輯 ER 關係圖
🏢 buildings
PKiduuid
nametext
addresstext
plan_tierenum
metadatajsonb
👤 profiles
PKiduuid→auth.users
FKbuilding_iduuid→buildings
roleenum
full_nametext
phonetext
metadatajsonb
🏠 units
PKiduuid
FKbuilding_iduuid→buildings
unit_numbertext
floorint
area_sqmnumeric(10,2)
🔑balancenumeric(19,4)
metadatajsonb
📄 invoices
PKiduuid
FKbuilding_iduuid→buildings
FKunit_iduuid→units
🔑amountnumeric(19,4)
🔑paid_amountnumeric(19,4)
statusenum
due_datedate
billing_perioddaterange
metadatajsonb
💰 payments
PKiduuid
FKbuilding_iduuid→buildings
FKunit_iduuid→units
FKguard_iduuid→profiles
🔑amountnumeric(19,4)
methodenum
statusenum
metadatajsonb
🔗 payment_allocations
PKiduuid
FKbuilding_iduuid→buildings
FKpayment_iduuid→payments
FKinvoice_iduuid→invoices
🔑allocated_amountnumeric(19,4)
🏦 petty_cash
PKiduuid
FKbuilding_iduuid→buildings
FKguard_iduuid→profiles
FKapproved_byuuid→profiles
🔑amountnumeric(19,4)
categorytext
statusenum
receipt_urltext
metadatajsonb
⚙️ billing_configs
PKiduuid
FKbuilding_iduuid→buildings
unit_typeenum
fee_typeenum
🔑ratenumeric(19,4)
effective_fromdate
metadatajsonb
1.2 完整 DDL Schema(PostgreSQL)
SQL migrations/001_initial_schema.sql
-- ============================================================
-- 大樓財務雲端管理平台 — 完整資料庫 Schema
-- PostgreSQL 15+ (Supabase)
-- 規則:所有金額欄位使用 NUMERIC(19,4),嚴禁 FLOAT/DOUBLE
-- ============================================================

-- ① 啟用必要擴充套件
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -- 模糊搜尋支援

-- ② 自定義 ENUM 類型
CREATE TYPE user_role AS ENUM ('committee_admin', 'guard', 'resident');
CREATE TYPE plan_tier AS ENUM ('free', 'pro', 'enterprise');
CREATE TYPE unit_type AS ENUM ('residential', 'commercial', 'parking', 'storage');
CREATE TYPE invoice_status AS ENUM ('pending', 'partial', 'paid', 'overdue', 'voided');
CREATE TYPE payment_method AS ENUM ('cash', 'transfer', 'check');
CREATE TYPE payment_status AS ENUM ('completed', 'pending_review', 'refunded', 'voided');
CREATE TYPE petty_cash_status AS ENUM ('pending', 'approved', 'rejected');
CREATE TYPE fee_type AS ENUM ('fixed', 'per_sqm');

-- ③ buildings — 多租戶核心表(每棟大樓一個 Workspace)
CREATE TABLE public.buildings (
  id              uuid          PRIMARY KEY DEFAULT uuid_generate_v4(),
  name            text          NOT NULL,
  address         text          NOT NULL,
  total_units     integer       NOT NULL DEFAULT 0,
  plan_tier       plan_tier     NOT NULL DEFAULT 'free',
  is_active       boolean       NOT NULL DEFAULT true,
  timezone        text          NOT NULL DEFAULT 'Asia/Taipei',
  settings        jsonb         NOT NULL DEFAULT '{}',  -- 大樓級設定
  metadata        jsonb         NOT NULL DEFAULT '{}',  -- 擴充欄位
  created_at      timestamptz   NOT NULL DEFAULT now(),
  updated_at      timestamptz   NOT NULL DEFAULT now()
);

-- ④ profiles — 擴展 Supabase auth.users,關聯使用者角色
CREATE TABLE public.profiles (
  id              uuid          PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
  building_id     uuid          NOT NULL REFERENCES buildings(id) ON DELETE CASCADE,
  role            user_role     NOT NULL,
  full_name       text          NOT NULL,
  phone           text,
  avatar_url      text,
  is_active       boolean       NOT NULL DEFAULT true,
  last_login_at   timestamptz,
  metadata        jsonb         NOT NULL DEFAULT '{}',
  created_at      timestamptz   NOT NULL DEFAULT now(),
  updated_at      timestamptz   NOT NULL DEFAULT now()
);

-- ⑤ units — 住戶單位(每戶一筆,balance 為核心財務狀態)
CREATE TABLE public.units (
  id              uuid            PRIMARY KEY DEFAULT uuid_generate_v4(),
  building_id     uuid            NOT NULL REFERENCES buildings(id),
  unit_number     text            NOT NULL,                -- 例:"3F-01"
  floor           integer,
  unit_type       unit_type       NOT NULL DEFAULT 'residential',
  area_sqm        numeric(10,2),                          -- 坪數(可選)
  owner_profile_id uuid           REFERENCES profiles(id),
  resident_profile_id uuid        REFERENCES profiles(id),
  -- ⚠️ 核心財務欄位:正數=預繳,負數=欠費,嚴禁使用 FLOAT
  balance         numeric(19,4)  NOT NULL DEFAULT 0.0000,
  is_active       boolean         NOT NULL DEFAULT true,
  metadata        jsonb           NOT NULL DEFAULT '{}',
  created_at      timestamptz     NOT NULL DEFAULT now(),
  updated_at      timestamptz     NOT NULL DEFAULT now(),
  UNIQUE (building_id, unit_number)  -- 同棟大樓內戶號唯一
);

-- ⑥ billing_configs — 計費設定(支援多類型費率)
CREATE TABLE public.billing_configs (
  id              uuid          PRIMARY KEY DEFAULT uuid_generate_v4(),
  building_id     uuid          NOT NULL REFERENCES buildings(id),
  name            text          NOT NULL,              -- 費用名稱,如"管理費"
  unit_type       unit_type     NOT NULL,
  fee_type        fee_type      NOT NULL DEFAULT 'fixed',
  rate            numeric(19,4) NOT NULL,             -- 月費金額或每坪單價
  effective_from  date          NOT NULL,
  effective_to    date,                                -- NULL = 無限期有效
  is_active       boolean       NOT NULL DEFAULT true,
  metadata        jsonb         NOT NULL DEFAULT '{}',
  created_at      timestamptz   NOT NULL DEFAULT now()
);

-- ⑦ invoices — 帳單(FIFO 沖帳的主體)
CREATE TABLE public.invoices (
  id                  uuid            PRIMARY KEY DEFAULT uuid_generate_v4(),
  building_id         uuid            NOT NULL REFERENCES buildings(id),
  unit_id             uuid            NOT NULL REFERENCES units(id),
  billing_config_id   uuid            REFERENCES billing_configs(id),
  invoice_number      text            NOT NULL,          -- 系統產生,如"INV-2025-03-001"
  billing_period      daterange       NOT NULL,          -- 計費期間
  due_date            date            NOT NULL,          -- FIFO 排序的關鍵欄位
  amount              numeric(19,4)  NOT NULL,
  paid_amount         numeric(19,4)  NOT NULL DEFAULT 0.0000,
  status              invoice_status  NOT NULL DEFAULT 'pending',
  description         text,
  metadata            jsonb           NOT NULL DEFAULT '{}',
  created_by          uuid            REFERENCES profiles(id),
  voided_at           timestamptz,
  voided_by           uuid            REFERENCES profiles(id),
  created_at          timestamptz     NOT NULL DEFAULT now(),
  updated_at          timestamptz     NOT NULL DEFAULT now(),
  UNIQUE (building_id, invoice_number),
  -- 確保金額非負
  CONSTRAINT invoices_amount_positive     CHECK (amount > 0),
  CONSTRAINT invoices_paid_not_exceed     CHECK (paid_amount <= amount),
  CONSTRAINT invoices_paid_non_negative   CHECK (paid_amount >= 0)
);

-- ⑧ payments — 收款紀錄(保全登記的原始收款)
CREATE TABLE public.payments (
  id              uuid            PRIMARY KEY DEFAULT uuid_generate_v4(),
  building_id     uuid            NOT NULL REFERENCES buildings(id),
  unit_id         uuid            NOT NULL REFERENCES units(id),
  guard_id        uuid            NOT NULL REFERENCES profiles(id), -- 經手人
  amount          numeric(19,4)  NOT NULL,
  method          payment_method  NOT NULL,
  status          payment_status  NOT NULL DEFAULT 'completed',
  notes           text,
  reference_no    text,               -- 轉帳單號
  paid_at         timestamptz     NOT NULL DEFAULT now(),
  metadata        jsonb           NOT NULL DEFAULT '{}',
  created_at      timestamptz     NOT NULL DEFAULT now(),
  CONSTRAINT payments_amount_positive CHECK (amount > 0)
);

-- ⑨ payment_allocations — FIFO 沖帳明細(核心關係表)
CREATE TABLE public.payment_allocations (
  id                uuid            PRIMARY KEY DEFAULT uuid_generate_v4(),
  building_id       uuid            NOT NULL REFERENCES buildings(id),
  payment_id        uuid            NOT NULL REFERENCES payments(id),
  invoice_id        uuid            NOT NULL REFERENCES invoices(id),
  allocated_amount  numeric(19,4)  NOT NULL,
  created_at        timestamptz     NOT NULL DEFAULT now(),
  CONSTRAINT allocations_positive CHECK (allocated_amount > 0)
);

-- ⑩ petty_cash — 零用金支出(保全登記,管委員審核)
CREATE TABLE public.petty_cash (
  id              uuid                PRIMARY KEY DEFAULT uuid_generate_v4(),
  building_id     uuid                NOT NULL REFERENCES buildings(id),
  guard_id        uuid                NOT NULL REFERENCES profiles(id),
  amount          numeric(19,4)      NOT NULL,
  category        text                NOT NULL,
  description     text                NOT NULL,
  receipt_url     text,
  status          petty_cash_status   NOT NULL DEFAULT 'pending',
  approved_by     uuid                REFERENCES profiles(id),
  approved_at     timestamptz,
  metadata        jsonb               NOT NULL DEFAULT '{}',
  created_at      timestamptz         NOT NULL DEFAULT now()
);

-- ============================================================
-- 索引設計(效能優化)
-- ============================================================
CREATE INDEX idx_profiles_building ON profiles(building_id);
CREATE INDEX idx_units_building ON units(building_id);
CREATE INDEX idx_units_balance ON units(building_id, balance); -- 欠費查詢
CREATE INDEX idx_invoices_unit_due ON invoices(unit_id, due_date); -- FIFO 排序
CREATE INDEX idx_invoices_status ON invoices(building_id, status);
CREATE INDEX idx_payments_unit ON payments(unit_id, paid_at);
CREATE INDEX idx_payments_guard ON payments(guard_id); -- 保全稽核查詢
CREATE INDEX idx_allocations_payment ON payment_allocations(payment_id);
CREATE INDEX idx_allocations_invoice ON payment_allocations(invoice_id);
CREATE INDEX idx_petty_cash_guard ON petty_cash(building_id, guard_id);

-- GIN 索引:模糊搜尋住戶姓名
CREATE INDEX idx_profiles_name_trgm  ON profiles USING GIN(full_name gin_trgm_ops);
CREATE INDEX idx_units_number_trgm   ON units    USING GIN(unit_number gin_trgm_ops);

-- updated_at 自動更新 Trigger
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = now();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER set_updated_at_buildings
  BEFORE UPDATE ON buildings
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER set_updated_at_units
  BEFORE UPDATE ON units
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER set_updated_at_invoices
  BEFORE UPDATE ON invoices
  FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
1.3 關聯說明表
關聯類型說明業務意義
buildings → profiles1 : Nbuilding_id FK一棟大樓有多個用戶(管委員/保全/住戶)
buildings → units1 : Nbuilding_id FK一棟大樓有多個住戶單位
units → invoices1 : Nunit_id FK一個單位可有多張帳單(歷史積累)
units → payments1 : Nunit_id FK一個單位的所有繳費紀錄
payments → payment_allocations1 : Npayment_id FK一筆收款可被拆分抵扣多張帳單(FIFO)
invoices → payment_allocations1 : Ninvoice_id FK一張帳單可被多筆收款部分抵扣
profiles → payments1 : Nguard_id FK追蹤每筆收款的經手保全
D2

FIFO 沖帳演算法

First-In-First-Out Auto-Reconciliation Algorithm

2.1 演算法流程圖
🟢 保全提交收款請求 (unit_id, amount, method, guard_id)
① 建立 payments 紀錄(status='completed')
② 查詢該 unit 所有未結清帳單,依 due_date ASC 排序(FIFO)
WHERE status IN ('pending','partial','overdue') AND unit_id = ?
③ 初始化:remaining_amount = payment.amount
remaining_amount > 0 AND 有未結帳單?
✅ 是
④ 取最舊帳單 invoice = invoices[0]
計算可用金額:available = invoice.amount - invoice.paid_amount
remaining ≥ available?
✅ 全額抵扣
allocated = available
invoice.status = 'paid'
❌ 部分抵扣
allocated = remaining
invoice.status = 'partial'
⑤ 建立 payment_allocations 紀錄
更新 invoice.paid_amount
remaining -= allocated
移至下一張帳單
❌ 否(沒有未結帳單)
⑥ 剩餘金額計入預繳
unit.balance += remaining_amount
⑦ 更新 unit.balance(增加付款總額,已在帳單抵扣中體現)
🔴 COMMIT 交易 / 回傳沖帳結果摘要
2.2 完整 PostgreSQL 預存程序實作
SQL — PL/pgSQL functions/process_payment_fifo.sql
-- ============================================================
-- FIFO 沖帳核心預存程序
-- 呼叫方式:SELECT * FROM process_payment_fifo(p_building_id, p_unit_id, p_amount, p_method, p_guard_id);
-- 所有操作在單一 DB 交易中完成,確保原子性 (Atomicity)
-- ============================================================

CREATE OR REPLACE FUNCTION process_payment_fifo(
  p_building_id   uuid,
  p_unit_id       uuid,
  p_amount        numeric,
  p_method        payment_method,
  p_guard_id      uuid,
  p_notes         text DEFAULT NULL,
  p_reference_no  text DEFAULT NULL
)
RETURNS jsonb
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
  v_payment_id       uuid;
  v_remaining        numeric(19,4);
  v_invoice          RECORD;
  v_available        numeric(19,4);
  v_allocated        numeric(19,4);
  v_allocation_count integer := 0;
  v_result           jsonb;
  v_allocations      jsonb := '[]';

BEGIN
  -- ① 前置驗證:確認 unit 屬於該 building(多租戶安全檢查)
  IF NOT EXISTS (
    SELECT 1 FROM units
    WHERE id = p_unit_id AND building_id = p_building_id
  ) THEN
    RAISE EXCEPTION 'Unit does not belong to building: %', p_unit_id;
  END IF;

  -- ② 確認金額合法
  IF p_amount <= 0 THEN
    RAISE EXCEPTION 'Payment amount must be positive, got: %', p_amount;
  END IF;

  -- ③ 建立 payments 紀錄(鎖定此筆收款)
  INSERT INTO payments (
    building_id, unit_id, guard_id, amount,
    method, status, notes, reference_no
  ) VALUES (
    p_building_id, p_unit_id, p_guard_id, p_amount,
    p_method, 'completed', p_notes, p_reference_no
  )
  RETURNING id INTO v_payment_id;

  -- ④ 初始化剩餘金額
  v_remaining := p_amount;

  -- ⑤ FIFO 核心迴圈:依 due_date ASC 取得所有未結清帳單
  FOR v_invoice IN
    SELECT id, amount, paid_amount, status
    FROM   invoices
    WHERE  unit_id      = p_unit_id
      AND  building_id  = p_building_id
      AND  status      IN ('pending', 'partial', 'overdue')
    ORDER BY due_date ASC    -- ← FIFO 關鍵排序
    FOR UPDATE               -- ← 悲觀鎖定,防止並發衝突
  LOOP
    EXIT WHEN v_remaining <= 0;

    -- 計算此帳單的可抵扣金額
    v_available := v_invoice.amount - v_invoice.paid_amount;

    IF v_available <= 0 THEN
      CONTINUE;  -- 跳過已結清的帳單(防止資料不一致)
    END IF;

    -- 決定本次抵扣金額
    v_allocated := LEAST(v_remaining, v_available);

    -- 建立沖帳明細紀錄
    INSERT INTO payment_allocations (
      building_id, payment_id, invoice_id, allocated_amount
    ) VALUES (
      p_building_id, v_payment_id, v_invoice.id, v_allocated
    );

    -- 更新帳單 paid_amount 與狀態
    UPDATE invoices
    SET
      paid_amount = paid_amount + v_allocated,
      status = CASE
        WHEN (paid_amount + v_allocated) >= amount THEN 'paid'::invoice_status
        ELSE 'partial'::invoice_status
      END,
      updated_at = now()
    WHERE id = v_invoice.id;

    -- 累積沖帳結果(回傳用)
    v_allocations := v_allocations || jsonb_build_object(
      'invoice_id', v_invoice.id,
      'allocated', v_allocated
    );

    v_remaining := v_remaining - v_allocated;
    v_allocation_count := v_allocation_count + 1;
  END LOOP;

  -- ⑥ 更新 unit.balance(全量收款 + 原始餘額 - 所有已結清帳單的影響)
  -- balance 語義:正=預繳,負=欠費
  -- 由於帳單產生時 balance 已扣減,這裡只需加上收款金額
  UPDATE units
  SET
    balance     = balance + p_amount,
    updated_at  = now()
  WHERE id = p_unit_id;

  -- ⑦ 組裝回傳結果
  v_result := jsonb_build_object(
    'payment_id',         v_payment_id,
    'total_paid',         p_amount,
    'applied_to_invoices', p_amount - v_remaining,
    'prepaid_credit',     v_remaining,     -- 若 > 0 表示預繳增加
    'invoices_cleared',   v_allocation_count,
    'allocations',        v_allocations
  );

  RETURN v_result;

-- ⑧ 例外處理:任何錯誤都 ROLLBACK 整個交易
EXCEPTION
  WHEN OTHERS THEN
    RAISE EXCEPTION 'FIFO payment failed: % %', SQLERRM, SQLSTATE;
END;
$$;
2.3 Next.js Server Action 呼叫範例
TypeScript app/actions/payment.ts
'use server'

import { createServerClient } from '@supabase/ssr'
import { Decimal }           from 'decimal.js'
import { revalidatePath }    from 'next/cache'
import { z }                 from 'zod'

// ① Zod Schema 驗證收款輸入
const PaymentSchema = z.object({
  unitId:      z.string().uuid(),
  amount:      z.string().refine(v => new Decimal(v).gt(0), '金額必須大於零'),
  method:      z.enum(['cash', 'transfer', 'check']),
  notes:       z.string().optional(),
  referenceNo: z.string().optional(),
})

export async function processPaymentAction(formData: FormData) {
  // ② 解析並驗證表單資料
  const raw = Object.fromEntries(formData.entries())
  const parsed = PaymentSchema.safeParse(raw)

  if (!parsed.success) {
    return { error: parsed.error.flatten() }
  }

  const { unitId, amount, method, notes, referenceNo } = parsed.data

  // ③ 使用 Decimal.js 確保精準度
  const preciseAmount = new Decimal(amount).toFixed(4)

  const supabase = await createServerClient(...)

  // ④ 取得目前登入保全資訊
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) return { error: '未授權' }

  const { data: profile } = await supabase
    .from('profiles')
    .select('building_id, role')
    .eq('id', user.id)
    .single()

  // ⑤ 確認為 guard 或 committee_admin
  if (!profile || !['guard', 'committee_admin'].includes(profile.role)) {
    return { error: '無收款權限' }
  }

  // ⑥ 呼叫 FIFO 預存程序(在 DB 交易中執行)
  const { data: result, error } = await supabase
    .rpc('process_payment_fifo', {
      p_building_id:  profile.building_id,
      p_unit_id:      unitId,
      p_amount:       preciseAmount,
      p_method:       method,
      p_guard_id:     user.id,
      p_notes:        notes ?? null,
      p_reference_no: referenceNo ?? null,
    })

  if (error) {
    return { error: error.message }
  }

  // ⑦ 重新驗證相關頁面快取
  revalidatePath(`/guard/units/${unitId}`)
  revalidatePath(`/admin/invoices`)

  return { success: true, data: result }
}
2.4 FIFO 計算情境範例
📋
情境:住戶 3F-01 有 3 張未結清帳單,現繳交 NT$8,000 現金。
帳單 ID到期日帳單金額已繳未繳FIFO 抵扣沖帳後狀態
INV-2025-012025-01-31NT$3,000NT$0NT$3,000 -NT$3,000 ✓✅ 已結清
INV-2025-022025-02-28NT$3,000NT$1,500NT$1,500 -NT$1,500 ✓✅ 已結清
INV-2025-032025-03-31NT$3,000NT$0NT$3,000 -NT$2,500 ✓⚠️ 部分繳清
✨ 預繳餘額- +NT$1,000存入 balance
結果:INV-2025-01 和 INV-2025-02 完全結清,INV-2025-03 仍欠 NT$500,剩餘 NT$1,000 計入預繳。unit.balance = -500(仍欠費)+ 8000(收款)= +1000 ... 此範例最終 balance 從 -4500 → -4500+8000 = 3500 並非如此,正確計算由 DB 維護。
D3

RLS 資料安全策略

Row Level Security — Supabase PostgreSQL

⚠️
核心原則:所有資料表均啟用 RLS,並設定 ALTER TABLE ... FORCE ROW LEVEL SECURITY。即便 service_role 直接查詢,也必須通過策略。Service Role 僅在必要的 Server Action 中使用,且需 SECURITY DEFINER 函式包裝。
SQL — RLS Policies migrations/002_rls_policies.sql
-- ============================================================
-- Row Level Security 策略設計
-- 核心設計原則:
--   1. 所有表都以 building_id 做租戶隔離
--   2. 使用 Helper Function 減少重複邏輯
--   3. Guard 角色只能看本人操作的資料
-- ============================================================

-- ① Helper Functions(減少重複邏輯)

-- 取得目前登入用戶的 building_id
CREATE OR REPLACE FUNCTION get_user_building_id()
RETURNS uuid
LANGUAGE SQL SECURITY DEFINER STABLE
AS $$
  SELECT building_id
  FROM   public.profiles
  WHERE  id = auth.uid()
$$;

-- 取得目前登入用戶的角色
CREATE OR REPLACE FUNCTION get_user_role()
RETURNS user_role
LANGUAGE SQL SECURITY DEFINER STABLE
AS $$
  SELECT role
  FROM   public.profiles
  WHERE  id = auth.uid()
$$;

-- 確認是否為 Committee Admin
CREATE OR REPLACE FUNCTION is_admin()
RETURNS boolean
LANGUAGE SQL SECURITY DEFINER STABLE
AS $$
  SELECT EXISTS (
    SELECT 1 FROM public.profiles
    WHERE id = auth.uid() AND role = 'committee_admin'
  )
$$;

-- ============================================================
-- 啟用所有資料表的 RLS
-- ============================================================
ALTER TABLE buildings           ENABLE ROW LEVEL SECURITY;
ALTER TABLE profiles            ENABLE ROW LEVEL SECURITY;
ALTER TABLE units               ENABLE ROW LEVEL SECURITY;
ALTER TABLE billing_configs     ENABLE ROW LEVEL SECURITY;
ALTER TABLE invoices            ENABLE ROW LEVEL SECURITY;
ALTER TABLE payments            ENABLE ROW LEVEL SECURITY;
ALTER TABLE payment_allocations ENABLE ROW LEVEL SECURITY;
ALTER TABLE petty_cash          ENABLE ROW LEVEL SECURITY;

-- ============================================================
-- buildings RLS
-- ============================================================
CREATE POLICY "buildings_select_own_tenant"
  ON buildings FOR SELECT
  USING (id = get_user_building_id());

CREATE POLICY "buildings_update_admin_only"
  ON buildings FOR UPDATE
  USING  (id = get_user_building_id() AND is_admin())
  WITH CHECK (id = get_user_building_id());

-- ============================================================
-- profiles RLS
-- ============================================================
CREATE POLICY "profiles_select_same_building"
  ON profiles FOR SELECT
  USING (
    building_id = get_user_building_id()
    AND (
      is_admin()                      -- 管理員看所有
      OR id = auth.uid()             -- 看自己的
      OR get_user_role() = 'guard'   -- 保全可看其他保全姓名(顯示經手人用)
    )
  );

CREATE POLICY "profiles_update_own_or_admin"
  ON profiles FOR UPDATE
  USING (
    building_id = get_user_building_id()
    AND (is_admin() OR id = auth.uid())
  );

CREATE POLICY "profiles_insert_admin_only"
  ON profiles FOR INSERT
  WITH CHECK (
    building_id = get_user_building_id()
    AND is_admin()
  );

-- ============================================================
-- units RLS
-- ============================================================
CREATE POLICY "units_select_by_role"
  ON units FOR SELECT
  USING (
    building_id = get_user_building_id()
    AND (
      is_admin()                                   -- 管理員查看所有單位
      OR get_user_role() = 'guard'               -- 保全查看所有單位(搜尋需要)
      OR owner_profile_id    = auth.uid()         -- 住戶看自己的單位
      OR resident_profile_id = auth.uid()         -- 住戶看自己的單位
    )
  );

CREATE POLICY "units_write_admin_only"
  ON units FOR ALL
  USING (building_id = get_user_building_id() AND is_admin());

-- ============================================================
-- invoices RLS
-- ============================================================
CREATE POLICY "invoices_select_by_role"
  ON invoices FOR SELECT
  USING (
    building_id = get_user_building_id()
    AND (
      is_admin()
      -- 住戶只能看自己單位的帳單
      OR unit_id IN (
        SELECT id FROM units
        WHERE owner_profile_id = auth.uid()
           OR resident_profile_id = auth.uid()
      )
    )
  );

-- 注意:Guard 不能直接 SELECT invoices,FIFO 函式以 SECURITY DEFINER 執行

-- ============================================================
-- payments RLS(Guard 只能看自己的收款,Admin 看全部)
-- ============================================================
CREATE POLICY "payments_select_by_role"
  ON payments FOR SELECT
  USING (
    building_id = get_user_building_id()
    AND (
      is_admin()
      OR guard_id = auth.uid()   -- Guard 只看自己的紀錄
      OR unit_id IN (             -- 住戶看自己單位的付款
        SELECT id FROM units
        WHERE owner_profile_id = auth.uid()
           OR resident_profile_id = auth.uid()
      )
    )
  );

-- Guard 無法直接 INSERT payments;透過 process_payment_fifo() 函式執行

-- ============================================================
-- petty_cash RLS
-- ============================================================
CREATE POLICY "petty_cash_select"
  ON petty_cash FOR SELECT
  USING (
    building_id = get_user_building_id()
    AND (is_admin() OR guard_id = auth.uid())
  );

CREATE POLICY "petty_cash_insert_guard"
  ON petty_cash FOR INSERT
  WITH CHECK (
    building_id = get_user_building_id()
    AND guard_id = auth.uid()
    AND get_user_role() = 'guard'
    AND status = 'pending'  -- Guard 只能建立待審核的
  );

CREATE POLICY "petty_cash_update_admin"
  ON petty_cash FOR UPDATE
  USING (building_id = get_user_building_id() AND is_admin());
3.1 RLS 策略摘要表
資料表SELECTINSERTUPDATEDELETE
buildings同棟所有角色僅 Service RoleAdmin 同棟禁止
profilesAdmin 全棟;Guard 有限欄位;Resident 僅本人Admin 同棟Admin 或本人Admin
unitsAdmin 全部;Guard 全部(搜尋需要);Resident 本戶AdminAdminAdmin
invoicesAdmin 全部;Resident 本戶帳單Admin(批量產生)FIFO 函式Admin(作廢)
paymentsAdmin;Guard 本人;Resident 本戶FIFO 函式Admin(退款)禁止
payment_allocationsAdmin;Resident 本戶FIFO 函式禁止禁止
petty_cashAdmin;Guard 本人Guard(pending 狀態)Admin(審核)禁止
D4

UI/UX 設計規範

保全平板優先設計 + 管委員 Web Dashboard

4.1 設計系統規範
🎨
色彩系統 (Color Tokens)
主色調
#1e40af — Primary Blue(主品牌色)
#3b82f6 — Blue 500(互動元素)
語意色
#16a34a — Success(預繳/已繳)
#dc2626 — Danger(欠費/逾期)
#f59e0b — Warning(部分繳/待審)
#475569 — Neutral(次要文字)
📐
間距與字型系統
字型家族
Noto Sans TC — 主要介面字型(繁中)
Inter — 數字與英文(金額顯示)
觸控尺寸(平板優化)
按鈕最小高度:48px
觸控目標最小:48×48px
列表行高(平板):64px
主要操作按鈕字號:16px
金額顯示字號:24-32px(Bold)
4.2 保全收款介面設計規範(平板優先)
保全平板介面 — 收款流程
📱 畫面 1/3 — 搜尋住戶
🔍
輸入姓名 / 戶號 / 電話...
🏠
陳小明
3F-01 · 電話 0912-XXX-XXX
欠費 -$1,500
3 張待繳
🏠
王大明
3F-02 · 電話 0923-XXX-XXX
預繳 +$3,000
💰 畫面 2/3 — 確認收款
住戶
陳小明 · 3F-01
當前欠費:-NT$1,500
未結帳單
2025年01月管理費NT$3,000
收款金額
NT$ 3,000
💵 現金
🏦 轉帳
✅ 確認收款 NT$3,000
📋
UX 設計原則:保全收款流程嚴格控制在 3 個畫面以內。 ① 搜尋住戶(自動完成)→ ② 確認金額+方式 → ③ 成功畫面(含印單選項)。 整個流程設計目標:30 秒內完成,所有按鈕觸控面積 ≥ 48px。
4.3 各角色路由架構
TypeScript app/(route structure)
// ============================================================
// Next.js 15 App Router 路由架構
// ============================================================

app/
├── (auth)/                         // 未登入用戶
│   ├── login/                     // 統一登入頁面(Supabase Auth)
│   └── register/                 // 大樓自助註冊
│
├── (guard)/                        // 🛡️ 保全專區(平板優先 UI)
│   ├── layout.tsx               // 保全 Layout(簡潔、大按鈕)
│   ├── dashboard/               // 保全首頁(快速操作入口)
│   ├── collect/                  // 收款流程
│   │   ├── search/               // Step 1: 搜尋住戶
│   │   ├── [unitId]/             // Step 2: 確認收款
│   │   └── success/             // Step 3: 收款成功
│   ├── petty-cash/new/          // 登記零用金支出
│   └── history/                  // 個人操作紀錄
│
├── (admin)/                        // 👑 管委員後台(桌面/平板)
│   ├── layout.tsx               // 管理員 Layout(側欄導覽)
│   ├── dashboard/               // 財務總覽儀表板
│   ├── units/                    // 住戶單位管理
│   │   ├── page.tsx             // 單位列表(含餘額)
│   │   └── [unitId]/page.tsx  // 單位詳情 + 帳單歷史
│   ├── invoices/                 // 帳單管理
│   │   ├── page.tsx             // 帳單列表
│   │   └── generate/           // 批量產生帳單
│   ├── payments/                 // 收款紀錄 + 稽核
│   ├── petty-cash/              // 零用金審核
│   ├── reports/                  // 財務報表
│   ├── staff/                    // 保全帳號管理
│   └── settings/                // 大樓設定 + 計費費率
│
└── (resident)/                     // 🏠 住戶自服務
    ├── layout.tsx
    ├── dashboard/               // 我的帳務總覽
    ├── invoices/                 // 我的帳單明細
    ├── payments/                 // 我的繳費紀錄
    └── reports/                  // 大樓公開財報
4.4 響應式斷點策略
斷點尺寸範圍目標裝置適用介面特殊優化
sm640px+手機橫向住戶查詢介面單欄佈局
md768px+平板(9.7")🛡️ 保全收款介面大按鈕觸控,雙欄佈局
lg1024px+平板(12")保全 + 管委員側欄展開,三欄支援
xl1280px+桌面螢幕👑 管委員後台完整儀表板佈局
2xl1536px+大螢幕報表 + 稽核多視窗比較佈局
D5

API 設計規範

Next.js 15 Server Actions + Route Handlers

💡
本平台採用 Next.js 15 Server Actions 為主要資料變更方式,Route Handlers 用於公開 API 端點。Supabase SDK 在 Server Component 中直接呼叫,無需額外建立 REST API 層。
5.1 核心 Server Actions 規格
Action 名稱檔案路徑可呼叫角色說明
processPaymentActionapp/actions/payment.tsGuard, Admin觸發 FIFO 沖帳(呼叫 DB 函式)
generateInvoicesActionapp/actions/invoice.tsAdmin批量產生指定月份帳單
createPettyCashActionapp/actions/petty-cash.tsGuard登記零用金支出(含圖片上傳)
approvePettyCashActionapp/actions/petty-cash.tsAdmin審核/拒絕零用金申請
createUnitActionapp/actions/unit.tsAdmin建立住戶單位,連結住戶 Profile
createStaffActionapp/actions/staff.tsAdmin建立保全帳號(角色鎖定為 guard)
voidInvoiceActionapp/actions/invoice.tsAdmin作廢帳單(需反向更新餘額)
refundPaymentActionapp/actions/payment.tsAdmin退款並反轉 FIFO 沖帳記錄
5.2 公開 Route Handlers(住戶財報)
端點方法權限說明
/api/buildings/[id]/public-reportGETResident(同棟)取得大樓月度公開財報摘要
/api/units/[id]/statementGETResident(本戶)取得個人帳務對帳單(PDF 用)
5.3 統一錯誤回應格式
TypeScript types/api.ts
// 所有 Server Action 統一回傳格式
type ActionResult<T> = 
  | { success: true;  data: T }
  | { success: false; error: { code: string; message: string; details?: unknown } }

// 錯誤碼定義
const ErrorCodes = {
  UNAUTHORIZED:      'E001',  // 未授權
  FORBIDDEN:         'E002',  // 無此操作權限
  TENANT_MISMATCH:   'E003',  // 跨租戶資料存取
  INVALID_AMOUNT:    'E101',  // 金額格式錯誤
  DUPLICATE_INVOICE: 'E201',  // 重複產生帳單
  INVOICE_VOIDED:    'E202',  // 帳單已作廢
  PAYMENT_FAILED:    'E301',  // 沖帳失敗(DB 交易錯誤)
  UNIT_NOT_FOUND:    'E401',  // 找不到住戶單位
} as const
D6

部署與維運架構

Production Infrastructure

Vercel
Next.js 15 前端 + Server Actions
Edge Runtime
自動 CI/CD(GitHub)
🗄️
Supabase
PostgreSQL + Auth + RLS
Supabase Storage(收據照片)
Realtime(即時通知)
📧
Resend
交易郵件(帳單通知)
欠費催繳信件
管委員審核提醒
6.1 環境變數規格
ENV .env.local
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...
SUPABASE_SERVICE_ROLE_KEY=eyJhbGci...  # Server-only!絕不暴露前端
SUPABASE_JWT_SECRET=your-jwt-secret

# 郵件服務
RESEND_API_KEY=re_xxxx
RESEND_FROM_EMAIL=noreply@yourbuilding.app

# 應用設定
NEXT_PUBLIC_APP_URL=https://app.buildingfinance.tw
NEXT_PUBLIC_APP_NAME=大樓財務雲端管理平台
6.2 資料備份策略
🔄 自動備份(Supabase)
  • • Pro 方案:每日自動備份,保留 7 天
  • • Enterprise:Point-in-Time Recovery (PITR)
  • • 跨地區備援(選擇日本 ap-northeast-1)
🔐 安全規範
  • • 生產環境不得直接存取 service_role
  • • 所有 API 路由必須驗證 JWT
  • • 財務異動記錄 append-only(禁止刪除)
6.3 開發里程碑規劃
Sprint時間交付內容優先等級
Sprint 0Week 1-2DB Schema + RLS 策略 + Supabase 專案初始化P0 基礎
Sprint 1Week 3-4Supabase Auth 整合 + RBAC Middleware + 基礎頁面佈局P0 基礎
Sprint 2Week 5-6FIFO 沖帳引擎 + 保全收款介面(核心 MVP)P0 核心
Sprint 3Week 7-8管委員帳單管理 + 批量產生 + 欠費清單P0 核心
Sprint 4Week 9-10住戶自服務介面 + 公開財報 + 通知郵件P1
Sprint 5Week 11-12零用金管理 + 稽核報表 + PDF 匯出P1
Sprint 6Week 13-14效能優化 + E2E 測試 + 安全稽核 + 正式上線P2
🎯
文件完整性聲明:本 SRS/SDS 文件涵蓋完整的系統設計規格,包含資料庫 DDL、FIFO 演算法 PL/pgSQL 實作、RLS 安全策略、UI/UX 規範及 API 設計。開發者可直接依據本文件進行實作,無需額外澄清核心設計決策。
🏢
大樓財務雲端管理平台 SRS/SDS v1.0.0
© 2025 · 所有技術細節以最終開發實作為準 · 僅供內部開發使用