Flask 創建一個二手書籍線上購物平台-開發階段規劃

Flask 創建一個二手書籍線上購物平台-開發階段規劃

本系列文章是紀錄研究所軟體工程課程修課實作過程

專案時程規劃

考慮到組員在 11 月和 12 月有許多課程報告需要完成,我們將系統程式的開發時間拉長了 5 到 6 週。預計在 12 月搭建測試環境,並開放給負責系統測試的團隊成員進行測試。他們將在測試期間收集所有未通過的案例問題。在最後兩週,開發人員將解決這些問題。以下是我們的專案時程表。

專案時程規劃

什麼是 WBS?

工作分解結構(Work Breakdown Structure,簡稱 WBS)是在專案管理中常用的工具,把專案的工項分解成更小、更容易管理的部分。WBS基本上是一種樹狀結構,展示了專案的主要交付物,以及為了完成這些交付物所需的各種任務和活動。

WBS

我們使用這套架構來規劃分派給團隊成員的工作。在這個階段,我們的交付內容是開發程式。我們根據使用者案例圖和活動圖,規劃出大約 24 個 API 的開發任務。基於業務流程的順序性,我們討論了任務開發的優先順序,並在下面的 Level 和 WBS 圖表中標示出來,最後將這些任務分派給負責開發和 code review 的團隊成員。

專案WBS

程式碼提交與整合

此專案使用 GitLab 做程式碼儲存、提交、整合,採 Git flow 方式整合程式碼,開發階段步驟如下:

  1. 首先開發人員要從 dev 分支拉出 feature 分支
  2. 完成程式實作後,抓最新版 dev 分支,與自己的 feature 分支,以此避免提交 merge request 會有版本衝突發生
  3. 推 local feature 分支到 remote feature 分支
  4. 於 GitLab 發 merge request,將 feature 分支合併至 dev 分支
  5. 由 code reviewer 做程式碼審閱,確認無誤後同意合併
  6. 循返往復

Git flow

Flask 創建一個二手書籍線上購物平台-系統設計

Flask 創建一個二手書籍線上購物平台-系統設計

技術可行性評估

  1. 評估組員的技能樹,大部分組員有點 Python,且有使用過 Flask 框架的經驗,故使用Flask 框架。
  2. 考量到我們只有一個月的時間開發(不含測試),且同時要修課,我們決定使用 ORM ,原因是因為組員不需要額外寫 SQL,可以加快整體開發進度。
  3. 目前業界最新技術採用前後端分離,我們也是考量到時間,且組內的組員對 JavaScript 沒有到很熟悉,我們改由後端來渲染前端模板。
  4. 三層式架構雖然會增加開發時間,採用它的原因在於好測試,最主要這門課程著重在「測試」。

系統部署架構

硬體資訊

硬體 作業系統 安裝模組
Client PC Windows / MacOS/ Linux
Web Server AWS EC2 (Amazon Linux) nginx-1.23.4
Application Server AWS EC2 (Amazon Linux) Gunicorn 21.2.0
Database Server AWS RDS PostgreSQL 15

使用者會透過 PC 瀏覽我們二手書籍線上購物平台,期間發出的 Http request 會打到 Nginx 反向代理伺服器,再導向應用程式伺服器,在應用程式伺服器裡面運行 gunicorn 的網頁伺服器,如若涉及操作資料庫,會再到 PostgreSQL 做操作。

實際營運環境要用什麼樣的硬體規格,會在測試階段環節確認出來。

Image

系統軟體架構設計

系統使用 Python 的 Flask 網頁伺服器框架作為 MVC 框架,並且採用了三層式架構。首先是Controller層,負責接收和處理 Http request,同時處理與 View 的交互。第二層是Service層,專門負責處理商業邏輯,確保系統邏輯的一致性和有效性。最後一層是 ORM 層,主要負責操作 Model 並與 PostgreSQL 資料庫伺服器進行溝通。

選擇三層式架構的原因在於可以實現元件之間的解耦,提高模組的內聚力,同時為未來的開發工作提供方便,特別是在進行單元測試和整合測試時。這樣的結構使得不同層次的功能分明,開發人員能夠更容易地定位和處理相關模組的問題,同時也提升了代碼的可維護性和可擴展性。

模組版本資訊

1
2
3
4
5
1. Python version: 3.9
2. pytest: 7.4.3
3. Flask: 3.0.0
4. SQLAlchemy: 2.0.22
5. PostgreSQL version: 15

Image

檔案結構

在 Presentation layer 會放 Controller 模塊、跟網頁模板,在 Business layer 會放 Service 模塊,負責接收從 Controller 傳過來的資料,做完商業邏輯運算後,會使用 DBManager 模塊與 PostgreSQL 溝通操作。

Image

類別圖設計

Python 實務上不像 Java 會以類別包住 API,更常是直接在 py 檔上寫 function,所以我們 python 模塊視為類別,在類別圖上我們有特別標示 Module 字樣。

而這張類別圖是參考使用者案例圖所繪製出來的,我們將四大使用者案例轉換成 Python 模塊,細部使用者案例則設計 API 接口,承如上面所介紹的系統架構,以三層式設計,分別是 Controller、Service、DAO(ORM) 層。

大致上,負責開發的組員以這個架構下去做開發,更細部業務邏輯我們有記錄在系統設計書,提供可以組員開發做參考,在此不贅述。

Image

Flask 創建一個二手書籍線上購物平台-資料庫設計

Flask 創建一個二手書籍線上購物平台-資料庫設計

本系列文章是紀錄研究所軟體工程課程修課實作過程

資料模型設計

我們的資料庫包含8個實體,這些實體是根據使用者案例圖的設計而來。首先,我們從左上角開始進行說明。每位會員擁有一個「購物車」,而每個「購物車」可以包含多個「購物車品項」,這些「購物車品項」來自於「品項」實體,形成一對一的關係。

在二手平台中,我們從學長姐那裡收集書籍,將這些書籍的資訊登錄到「品項」實體中。由於這些品項有可能是同一本書,所以「品項」與「書籍」之間形成多對一的關係,這麼做的原因是為了區分提供者的身份。

回到前述,會員可以透過平台下單,因此每位會員可能擁有多張「訂單」,而每張訂單則包含多個「訂單商品」。「訂單商品」、「品項」、「書籍」這三個實體之間的關聯形成了訂單明細。

由於在會員模組中有一個「忘記密碼」的功能,我們計劃以系統分發token的方式來驗證會員身份,以便在後續進行密碼更改。因此,我們另外設計了一個實體,稱為「忘記密碼令牌」,以「會員的電子郵件地址」的屬性作為外部鍵,連接「會員」實體。由於我們限制系統中不允許相同的信箱重複註冊,這使得「忘記密碼令牌」和「會員」之間的實體關係保持為一對一。否則,在會員更改密碼時可能會影響到其他會員的密碼。

ERD

Image

ERD(雞爪圖)

Image

資料庫權限設計

PostgreSQL 為本專案選用關聯式資料庫管理系統

RBAC 模型(Role-Based Access Control:基於角色的訪問控制

在談論專案資料庫權限設計之前,我們不得不提及RBAC模型(基於角色的訪問控制)。RBAC模型是一種權限存取機制,由三個基本組成部分組成,即使用者、角色和許可權。這個模型強調誰(WHO)以什麼方式(HOW)可以對什麼(WHAT)進行存取。

以銀行貸款服務為例,承辦櫃員可以查詢顧客聯徵信用紀錄和初步審查貸款資料,最終由主管進行審核和撥款。因此,每個角色都擁有相對應的權責。我們的目標是使角色的權限最小化,以避免權限開放得太大。

同樣地,這種機制也可以應用在資料庫中,具有以下好處:

  1. 降低了潛在的安全風險: 如果受到SQL注入攻擊,這種機制可以防止系統使用的帳號擁有過大的權限,從而防止查詢到機敏資訊或竄改重要的資料,攻擊者僅能獲取到相應角色的權限。
  2. 細緻權限控制: 對於廠商或一般使用者,可以建立僅能讀取資料的帳號,而只有少數管理者能夠擁有寫入資料的權限,從而確保嚴格的讀寫權責的實施。
  3. 簡化權限管理:每個角色擁有特定的權限,無需針對每個使用者進行單獨的權限分配,只要分配對應的角色給使用者即可。

實作

我們將 8 張資料表創建在同一個 schema 底下,建立 rl_sel、rl_del、rl_upd、rl_ins,分別是可以對這 8 張資料表篩選、刪除、更新、新增的權限角色,再來是建立 app_001、it_001 這兩個可登入角色,app_001 是給系統使用的帳號,it_001 是給開發人員使用的帳號,此外這組帳號可以在這個 schema 新增、刪除資料庫物件。

如果後台系統資料表在另一個 schema 存放,會因為這套機制,可以有效地保護資料,不讓角色享有過大的權限,設計上,就可以成立 app_002 角色,只能對後台系統資料表操作,it_001 則可以跨 schema 對資料表操作。

Image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
-- 建立資料庫
CREATE DATABASE bookstore;
-- 建立 bookmazon schema
CREATE SCHEMA bookmazon;

-- 可以選資料表內容的角色
CREATE ROLE rl_sel;
-- 可以刪除資料表內容的角色
CREATE ROLE rl_del;
-- 可以更新資料表內容的角色
CREATE ROLE rl_upd;
-- 可以新增資料表內容的角色
CREATE ROLE rl_ins;

-- 為新增、刪除、修改、查詢賦予角色
GRANT SELECT ON TABLE bookmazon.book TO rl_sel;
GRANT DELETE ON TABLE bookmazon.book TO rl_del;
GRANT UPDATE ON TABLE bookmazon.book TO rl_upd;
GRANT INSERT ON TABLE bookmazon.book TO rl_ins;

-- 程式開發人員操作 db 的角色
CREATE ROLE user_001 WITH LOGIN CREATEDB CREATEROLE PASSWORD 'user_001';
GRANT rl_sel, rl_del, rl_upd, rl_ins to user_001;
-- 賦予 bookmazon schema 的使用權限
GRANT USAGE ON SCHEMA bookmazon to user_001;
-- 賦予可以 bookmazon schema 建立物件(trigger、function、procedure)的權限
GRANT CREATE ON SCHEMA bookmazon to user_001;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA bookmazon TO user_001;

-- 後端系統操作 db 的角色
CREATE ROLE app_001 WITH LOGIN PASSWORD 'app_001';
GRANT rl_sel, rl_del, rl_upd, rl_ins to app_001;
-- 賦予 bookmazon schema 的使用權限
GRANT USAGE ON SCHEMA bookmazon to app_001;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA bookmazon TO app_001;

資料庫表空間管理

什麼是資料庫表空間?

在 PostgreSQL,表空間意思是指資料庫的管理者可以自行定義一段檔案系統的路經,讓資料庫的物件都儲存在這個資料夾底下,爾後凡有資料庫的物件,例如:資料表、索引、視圖要找地方儲存的時候,那就可以引用資料庫的管理者所定義好的路徑進行資料儲存。

為什麼要做資料庫表空間管理?

  1. 如果要資料表的資料要擴張到別的地方(新增硬碟),可以在不同的分割區上建立資料表空間。
  2. 資料庫管理者可以控制 PostgreSQL 安裝的磁碟規畫,假設資料不斷地增長,原有的檔案系統空間不足時,可以在新掛載的檔案系統建立新的表空間。
  3. 允許資料庫管理者依資料庫物件特性的知識來優化效能,比較常查詢的資料可以放在固態硬碟上,相對於不常查詢的資料,就可以放在傳統硬碟上。

實務上,如何進行?

在 PostgreSQL,資料表會以檔案方式落地在預設目錄 $PGDATA/base/,檔案以資料庫物件 OID 命名,資料庫的大小取決於磁碟分割的大小。

為了做到資料庫表空間管理,我們選擇將資料改放在 /var/pgdata/bookmazon/,在下 PostgreSQL TABLESPACE 指令之前,我們先建立 /var/pgdata/bookmazon/ 資料夾出來,讓資料夾擁有者賦予給 postgres 角色(作業系統的角色),權限設為 700。

以上操作必須做,否則在下 PostgreSQL TABLESPACE 指令,會因為作業系統的檔案權限不足而失敗。

1
2
3
4
sudo mkdir /var/pgdata
sudo mkdir /var/pgdata/bookmazon/
sudo chown -R postgres:postgres /var/pgdata
sudo chmod -R 700 /var/pgdata

我們用了 PostgreSQL TABLESPACE 指令在 /var/pgdata/bookmazon/ 建立了表空間。

1
CREATE TABLESPACE OWNER postgres bookmazontbspace LOCATION '/var/pgdata/bookmazon/';

以建立 password_reset_tokens 資料表為例,在下 CREATE TABLE 指令後面會加上 TABLESPACE [表空間名稱],來表示建立的資料表的資料要落地在哪一個磁區。

1
2
3
4
5
6
7
8
9
CREATE TABLE bookmazon.password_reset_tokens (
id SERIAL NOT NULL
, user_email VARCHAR(255) NOT NULL
, token VARCHAR(255) NOT NULL
, token_status VARCHAR(1)
, update_datetime TIMESTAMP
, create_datetime TIMESTAMP
, CONSTRAINT pk_password_reset_tokens PRIMARY KEY (id)
) TABLESPACE bookmazontbspace;

參考來源

EnterproseDB Quickstart — 快速入門筆記

PostgreSQL 繁體中文手冊 - 23.6. Tablespaces

PostgreSQL 表空間使用詳解

Flask 創建一個二手書籍線上購物平台-系統需求與分析

Flask 創建一個二手書籍線上購物平台-系統需求與分析

本系列文章是紀錄研究所軟體工程課程修課實作過程

系統需求

1. 專案目的與動機

現代教育體系中,學生每學期都需要購買大量教科書,然而,許多書籍僅在短時間內使用,之後就被束之高閣。為了解決這個問題,我們致力於建立一個二手書交流平台,讓學長姊能夠輕鬆販賣二手書,同時讓學生購書的負擔降低。這不僅有助於節省金錢,更能實現地球永續發展,減少資源浪費。

2. 系統特色

a. 公開的平台,價格透明化

我們的平台注重透明度,讓購買者能夠清晰了解市場價格,避免不合理的高價販售。這種公開的交易環境有助於建立信任,提高整個系統的效能。

b. 第三方平台管理

為了確保平台的公正性和安全性,我們引入第三方平台管理機制。這將有助於處理潛在的糾紛,確保交易的順利進行,同時為使用者提供更多保障。

3. 使用者定位

a. 不再需要用到該教科書的同學

透過平台,這些同學能夠迅速輕鬆地轉手自己不再需要的教科書,獲得一些額外的收入,同時也促進了書籍的再利用。

b. 需要教科書卻無法負擔高額費用的同學

平台使這些同學能夠以更經濟實惠的價格獲得需要的教材,減輕了他們的經濟負擔,提高了教育資源的可及性。

c. 想擁有學長姐精華筆記的同學

除了書籍,我們的平台還提供學長姐的筆記販售功能,這滿足了一部分同學對於高質量學習材料的需求。

系統分析

使用者案例圖找出誰是系統的參與人、歸納出領域、並劃分系統內的領域邊界

我們與同學深入討論後,整理出了這套系統可能的各種使用者案例,並繪製了相對應的 Use Case Diagram。系統的使用者案例主要分為四大模組,包括會員模組、商品模組、訂單模組、以及購物車模組。

1. 會員模組

會員模組涵蓋了所有使用者在系統中的身份認證和相關功能。註冊、登入、修改個人資料等功能均屬於會員模組的範疇。

2. 商品模組

商品模組提供了訪客和會員查詢、瀏覽商品的功能。即使在未登入的情況下,訪客可以方便地瀏覽並查詢系統中的商品。會員作為訪客的一種。

3. 訂單模組

訂單模組與購物車模組之間存在高度相依性。這是因為結帳購物車這個使用者案例會延伸到新增訂單,因此兩者之間有著密切的關係。這一模組負責處理使用者的訂單相關操作,包括查看歷史訂單、取消訂單等功能。

4. 購物車模組

購物車模組負責處理使用者在購物過程中的相關操作。這包括將商品加入購物車、管理購物車內商品、進行結帳等功能。購物車模組的操作直接影響到訂單模組的運作。

這套系統的構想是,訪客可以在不登入的情況下瀏覽、查詢商品,而會員則包含在訪客的範疇內,因此可以享有訪客使用的功能。特別值得一提的是,訂單模組與購物車模組之間存在相對高的相依性,這是因為結帳購物車的案例會直接連接到新增訂單的流程中。

Image

資料建模分析

活動圖(泳道圖)可以拿來疏理各個使用者案例的動向,以及了解業務流程中參與人會是誰

我們透過活動圖以泳道的方式呈現了系統的業務流程。使用者以「訪客」或「會員」身份進入網站後,開始瀏覽並查詢商品。若在這過程中點擊網頁加入購物車,系統會先檢查會員是否已登入。若未登入,系統將引導至登入畫面;若已登入,則將商品成功加入購物車。

在查看購物車期間,會員擁有刪除商品或調整商品購買數量的權限。完成購物後,系統將自動生成訂單。會員隨後可在訂單列表中查看已下訂的訂單,並追蹤其進度。若欲取消訂單,會員需提交取消申請,經系統管理員審核後方可生效。

Image

循序圖是用細部拆解使用者案例的流程

接著我們挑出比較核心的 Use case 來繪製成循序圖,方便我們梳理資料流、與業務流程,圖上的物件有會員、前端介面、後端模組、以及資料庫,整理流程為登入、瀏覽商品、加入購物,最後到結帳。

登入流程

  1. 會員或訪客進入前端介面。
  2. 前端介面發送登入請求給後端模組。
  3. 後端模組驗證登入請求,若為會員,則檢查會員身份;若為訪客,則要求註冊。
  4. 登入結果回傳給前端介面。

瀏覽商品流程

  1. 會員或訪客透過前端介面發送瀏覽商品請求。
  2. 後端模組從資料庫中搜尋商品資訊。
  3. 商品資訊回傳給前端介面。

加入購物車流程

  1. 會員或訪客透過前端介面發送加入購物車請求。
  2. 後端模組驗證使用者身份,確認是否為登入狀態。
  3. 若為登入狀態且有庫存狀態下,將商品加入購物車,並把加入結果回傳給前端介面。
  4. 如果沒有庫存,回傳「暫無庫存」給前端介面。

結帳流程

  1. 會員透過前端介面發送結帳請求。
  2. 後端模組從購物車中搜尋預購買商品,在庫存數足夠狀態下,扣除購買商品的庫存數,若不足,回傳「暫無庫存」給前端介面。
  3. 會員的購物車的商品移除,將購物商品的資訊儲存為訂單。
  4. 訂單成立後,將訂單資訊儲存到資料庫。
  5. 回傳訂單資訊給前端介面。

Image

需求訪談(補充內容)

怎麼需求獲取?

在進行需求訪談之前,充分的事前準備是必要的。事前的準備包括深入了解相關背景知識。需求訪談通常應該針對真實的使用者進行,人數最好控制在 20 人以內。根據我們的案例,可以選擇不同系所的同學進行訪談,同時也應該尋求領域專家(例如老師或其他電商平台經營者)的意見。需要注意的是,並不是每位受訪者都能充分表達對於系統需求的看法,因此訪談者可能需要巧妙設計問題,引導受訪者表達他們對系統的需求。整體過程有點像摸象,透過了解需求來逐漸塑造系統的輪廓。

怎麼確認需求?

在需求訪談結束後,我們將收集到各種意見和功能需求。基於這些資料,我們可以進行資料建模,而 UML(統一建模語言)是一個強大的工具,用來繪製系統使用者案例、設計業務流程等。接著,我們可以再次召集使用者和領域專家來檢視繪製的 UML 圖,請他們確認這是否是他們期望的系統樣貌,這是為了確保我們所想的與使用者需求是否一致。

在實務操作中,需求訪談往往需要進行多輪,且顧客的時間寶貴。因此,在召開會議之前,確保準備工作充足至關重要。需求文件要等到顧客簽署後方能確認完畢,這樣的確認流程是為了保證最終的系統需求與使用者的期望保持一致。

[Heroku][Flask] Couldn't find that process type (web).

[Heroku][Flask] Couldn't find that process type (web).

問題描述

朋友想製造一個 LINE 機器人,自學學習撰寫 Python Flask Web Framework ,並部署到 Heroku 上,預期傳訊息時機器人會自動回覆相同的訊息內容,但卻沒有顯示在對話視窗內,於是拜託我幫他看。

首先要看的就是 Heroku console log ,下 heroku logs --tail 從裡面一定可以找出一些蛛絲馬跡,果不其然看到一些訊息,從日誌檔可得知程式是包版成功的,但很顯然程式發生錯誤, Web server 根本就沒有啟動,官方網站給出的解決方法是下 heroku ps:scale web=1 這組指令,以手動方式將 Web server 程序部署到他們的引擎 dyno 上。

Heroku 上日誌檔

1
2
2022-03-12T05:20:20.000000+00:00 app[api]: Build succeeded
2022-03-12T05:20:30.962092+00:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/favicon.ico" host=groupbuyingchatbot.herokuapp.com request_id=808970d4-836d-40e6-9f8a-1e3b3a999199 fwd="42.77.131.204" dyno= connect= service= status=503 bytes= protocol=https

Heroku H14 錯誤代碼解釋

1
2
3
4
5
6
7
H14 - No web dynos running
This is most likely the result of scaling your web dynos down to 0 dynos. To fix it, scale your web dynos to 1 or more dynos:

heroku ps:scale web=1
Use the heroku ps command to determine the state of your web dynos.

2010-10-06T21:51:37-07:00 heroku[router]: at=error code=H14 desc="No web processes running" method=GET path="/" host=myapp.herokuapp.com fwd=17.17.17.17 dyno= connect= service= status=503 bytes=

你以這樣就解決問題了嗎?並沒有!我下了這道指令回覆訊息仍是找不是 Web Server 的程序,後續我確認了 Python 有沒有語法上的錯誤、部署腳本、套件 requirements.txt,也嘗試在本機上執行,皆無問題。

1
2
3
gordonfang$ heroku ps:scale web=1
Scaling dynos... !
! couldn't find that process type (web).

解決方法

隔一天再次檢查才發現原來朋友寫的 Procfile 多了副檔名,是 Procfile.txt , Procfile 是 Heroku 的部署腳本,告訴他包版完後要跑哪一個檔案將 Web Server 啟動起來,重新調整完後機器人就能順利回覆訊息。

Image

1
2
3
4
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
message = text=event.message.text
line_bot_api.reply_message(event.reply_token, TextSendMessage(message))