背景
本文結合課程所學知識對工程實踐項目——抽獎系統,進行系統分析和設計,總結其中的軟件結構特點。
技術選型和運行環境
技術選型 Node.js + Express + MongoDB
- Why Node.js?
- Node.js 是一個基於 Chrome V8 引擎的 JavaScript 運行環境。
- Node.js 使用了一個事件驅動、非阻塞式 I/O 的模型,使其輕量又高效。
從 web 服務器開發的角度來看,Node 有很多好處:
- 卓越的性能表現!
- 代碼還是熟悉的老伙伴JavaScript。
- Node 包管理工具(NPM)提供了數十萬個可重用的工具包。
- Node.js 是可移植的,可運行於Windows、macOS、Linux等。
- 有活躍的第三方生態系統和開發者社區。
- Why Express?
Express 是最流行的 Node 框架,是許多其它流行 Node 框架 的底層庫。它提供了以下機制:
- 為不同 URL 路徑中使用不同 HTTP 動詞的請求(路由)編寫處理程序。
- 集成了“視圖”渲染引擎,以便通過將數據插入模板來生成響應。
- 設置常見 web 應用設置,比如用於連接的端口,以及渲染響應模板的位置。
- 在請求處理管道的任何位置添加額外的請求處理“中間件”。
- 極簡風格,通過各類兼容的中間件包解決了幾乎所有的 web 開發問題。
- Why MongoDB?
- MongoDB 是一個通用的、基於文檔的、分布式的數據庫,為雲計算時代的現代應用程序開發者而生,沒有數據庫比 MongoDB 在應用開發效率上更加高效。
- MongoDB 是一種文檔數據庫,也就是說 MongoDB 用類似 JSON 格式的文檔來存儲數據。目前普遍認為 JSON 格式是理解和存儲數據最自然的方式,JSON 格式比傳統的關系數據模型有更強大的數據表達能力。
運行環境
Node 可以在 Windows、macOS、Linux 的諸多發行版本或 Docker 等環境運行。Express 運行在 Node 環境中,因此可運行 Node 的平台均可運行 Express。
設計方案
MVC 架構/中介者模式
-
MVC 架構:
- Model(模型)用來封裝核心數據和功能,它獨立於特定的輸出表示和輸入行為,是執行某些任務的代碼。
- View(視圖)用來向用戶顯示信息,它獲得來自模型的數據,決定模型以什么樣的方式展示給用戶。
- Controller(控制器)作用於模型和視圖上,控制數據流向模型對象,並在數據變化時更新視圖。
-
中介者(Mediator)模式:定義一個中介對象來簡化原有對象之間的交互關系,降低系統中對象間的耦合度,使原有對象之間不必相互了解。采用“中介者模式”可以大大降低對象之間的耦合性,提高系統的靈活性。
Tips: 從面向對象的設計來看,MVC 是對象組合的綜合應用;從設計模式的角度看,Controller(控制器)是 Model(模型)和 View(視圖)之間的中介者(Mediator),是典型的中介者模式。
本項目也是基於 MVC 架構進行設計和開發,具體的設計參見下文的關鍵視圖,在本項目中,模型、視圖和控制器三者的具體作用如下:
- 模型:可以用來查找、創建、更新和刪除特定類型的對象。
- 控制器:從模型中獲取請求的數據,創建一個 HTML 頁面顯示出數據,並將頁面返回給用戶,以便在瀏覽器中查看。
- 視圖(模板):供控制器用來渲染數據。
Tips: MongoDB 數據庫中,每個模型都映射至一組文檔。
客戶-服務的架構風格
本項目采用客戶-服務的架構風格,也可描述為 Client/Server(C/S)和 Browser/Server(B/S)架構。客戶-服務模式的架構風格是指客戶代碼通過請求和應答的方式訪問或者調用服務代碼。在本項目中,請求和應答指的是 HTTP 協議中的 Request 和 Response,具體表現為:
- 對於管理員用戶,通過瀏覽器訪問該系統,瀏覽器發出 HTTP 請求,該系統返回 HTTP 響應;
- 對於普通用戶,通過微信 APP 訪問該系統,微信 APP 發出 HTTP 請求,該系統返回 HTTP 響應。
客戶-服務模式的架構風格具有典型的模塊化特征,降低了系統中客戶和服務構件之間耦合度,提高了服務構件的可重用性。
接口 API
本項目前后端接口調用是通過路由層來實現的,路由把需要支持的請求(以及請求 URL 中包含的任何信息)轉發到適當的控制器函數,可以分為三大部分:主頁、獎品和抽獎項,具體的URL和對應的控制器回調函數見下述代碼:
//主頁
router.get('/', home_controller.index);
router.get('/login', home_controller.login_get);
router.post('/login', home_controller.login_post);
router.get('/logout', home_controller.logout);
//獎品
router.get('/prizes', prize_controller.prize_list);
router.get('/prize/:id', prize_controller.prize_detail);
router.get('/prize/create', prize_controller.prize_create_get);
router.post('/prize/create', prize_controller.prize_create_post);
router.get('/prize/:id/delete', prize_controller.prize_delete_get);
router.post('/prize/:id/delete', prize_controller.prize_delete_post);
//抽獎項
router.get('/projects', project_controller.project_list);
router.get('/project/create', project_controller.project_create_get);
router.post('/project/create', project_controller.project_create_post);
router.get('/project/:id', project_controller.project_detail_get);
router.post('/project/:id', project_controller.project_detail_post);
router.get('/project/:id/update', project_controller.project_update_get);
router.post('/project/:id/update', project_controller.project_update_post);
router.get('/project/:id/delete', project_controller.project_delete_get);
router.post('/project/:id/delete', project_controller.project_delete_post);
router.get('/project/:id/qrcode', project_controller.project_qrcode);
關鍵視圖
分解視圖
依賴視圖
執行視圖
這里以創建抽獎項目為例給出執行視圖:
實現視圖
LotterySystem
├── app.js //應用入口
├── routes //路由
│ └── router.js
├── controllers //控制器
│ ├── homeController.js
│ ├── prizeController.js
│ └── projectController.js
├── models //模型
│ ├── prize.js
│ ├── project.js
│ └── user.js
├── views //視圖
│ ├── error.pug
│ ├── index.pug
│ ├── layout.pug
│ ├── login.pug
│ ├── lottery.pug
│ ├── prize_delete.pug
│ ├── prize_detail.pug
│ ├── prize_form.pug
│ ├── prize_list.pug
│ ├── project_delete.pug
│ ├── project_form.pug
│ ├── project_list.pug
│ └── qrcode.pug
├── public //靜態文件
│ ├── images
│ │ ├── Apple Watch Series 6.jpg
│ │ ├── Earphone Pro.jpg
│ │ ├── H445e0280b88f4f34a848238a848a687fw.png
│ │ ├── HUAWEI Mate 40 Pro+.jpg
│ │ ├── iPad Pro.jpg
│ │ ├── iPhone 12 Pro.jpg
│ │ ├── 小米 10 Ultra.jpg
│ │ └── 謝謝參與.jpg
│ ├── javascripts
│ │ ├── bootstrap.min.js
│ │ ├── bootstrap.min.js.map
│ │ ├── jquery-3.5.1.min.js
│ │ └── main.js
│ ├── qrcode
│ │ ├── 5fd738629f88ca228c49aa7e.jpg
│ │ ├── 5fd738c99f88ca228c49aa7f.jpg
│ │ └── 5fd9cbdbccc01d2180508f31.jpg
│ └── stylesheets
│ ├── bootstrap.min.css
│ ├── bootstrap.min.css.map
│ ├── login.css
│ ├── main.css
│ └── style.css
├── package.json //配置文件
└── README.md //簡介
部署視圖
數據庫設計
- 用戶模型(User)
const UserSchema = new Schema({
openid: {type: String, required: true},
projects: [{type: Schema.Types.ObjectId, ref: 'Project'}],
times: {type: [{type: Number}], required: true},
prizes: {type: [{type: [String]}], required: true}
});
- 獎品模型(Prize)
const PrizeSchema = new Schema({
name: {type: String, required: true, max: 100},
pictureUrl: {type: String, required: true}
});
- 抽獎項模型(Project)
const ProjectSchema = new Schema({
name: {type: String, required: true, max: 100},
times: {type: Number, min: 1, max: 10, required: true},
startTime: {type: Date, required: true},
finishTime: {type: Date, required: true},
prizeList: {type: [{type: Schema.Types.ObjectId, ref: 'Prize'}], required: true},
numList: {type: [{type: Number}], required: true},]
});
概念原型的核心工作機制
- 管理員用戶:通過該抽獎系統快速便捷地創建和發布抽獎項目;
- 普通用戶:通過微信掃碼參與抽獎項目。