本文為臺大資管團隊與長沂國際實業股份有限公司(COMEBUY)產學合作專案所開發之系統技術文件,由專案團隊撰寫,可提供公司人員在未來實際使用系統時參考。
本文共分為四節,依序說明系統環境、資料庫 schema、後端及前端撰寫。
---
## 1. 系統環境
### 1.1 系統安裝說明
1. 安裝 Node.js、Yarn、Python、miniconda、Postgresql 等必要軟體。
2. 在 `COMEBUY_SALES\frontend` 底下執行 `yarn` 指令,安裝所需套件。
```
// under COMEBUY_SALES\frontend
yarn
```
3. 建置前端 build 檔:在 `COMEBUY_SALES\frontend` 下執行 `yarn build`。
```
// under COMEBUY_SALES\frontend
yarn build
```
4. 建立一個 Postgresql database。
5. 編輯環境變數檔 `.env`,內容參照 `.env.example` 設定資料庫連接資訊以及希望伺服器運行之連接阜(在 `COMEBUY_SALES\frontend`、`COMEBUY_SALES\backend`、`COMEBUY_SALES\database` 下各有一個 `env` 檔,兩者皆需進行設定。)
6. 在 `COMEBUY_SALES\database` 下執行 `createTable.py` 檔建立資料庫的表格。
```
// under COMEBUY_SALES\database
python createTable.py
```
7. 安裝完 miniconda 後,在 window 左下角搜尋欄輸入 anaconda prompt,開啟後輸入 `conda init powershell`,關閉 anaconda prompt。

8. 接著回到 `COMEBUY_SALES\backend` 下,輸入以下指令。
```
// under COMEBUY_SALES\backend
conda create --name CB_NTU_2 python=3.9.16
conda activate CB_NTU_2
```
9. 安裝後端所需套件:在 `COMEBUY_SALES\backend` 下,輸入以下指令。
```
// under COMEBUY_SALES\backend
pip install -r requirements.txt
```
10. 啟動:在 `COMEBUY_SALES\backend` 底下執行以下指令,若無錯誤系統即安裝完成開始運行。
```
// under COMEBUY_SALES\backend
uvicorn main:app --host 0.0.0.0 --port 8000
```
## 2. 資料庫
### 2.1 資料庫說明
本專案使用 PostgreSQL。使用前須先安裝相關軟體。
### 2.2 資料表說明
資料表共 21 張,各表說明如下。
1. :::spoiler status
<table border="0" style="text-align:center;">
<tr>
<td colspan="6"><b style="font-size:20px">status</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>狀態名稱</td>
<td>Varchar</td>
<td>販賣中</td>
</tr>
</table>
2. :::spoiler categories
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">categories</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>種類名稱</td>
<td>Varchar</td>
<td>原葉鮮萃茶</td>
</tr>
</table>
3. :::spoiler drinks
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">drinks</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>飲品名稱</td>
<td>Varchar</td>
<td>海神</td>
</tr>
<tr>
<td>category</td>
<td>F</td>
<td>Y</td>
<td>種類</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>status</td>
<td>F</td>
<td>Y</td>
<td>狀態</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>comebuy_id</td>
<td></td>
<td>Y</td>
<td>公司飲品代號</td>
<td>Varchar</td>
<td>A020006</td>
</tr>
</table>
4. :::spoiler sweets
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">sweets</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>甜度名稱</td>
<td>Varchar</td>
<td>無糖</td>
</tr>
</table>
5. :::spoiler ices
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">ices</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>冰塊名稱</td>
<td>Varchar</td>
<td>去冰</td>
</tr>
</table>
6. :::spoiler tastes
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">tastes</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>口味名稱</td>
<td>Varchar</td>
<td>去籽</td>
</tr>
</table>
7. :::spoiler toppins
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">toppins</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>加料名稱</td>
<td>Varchar</td>
<td>布丁</td>
</tr>
</table>
8. :::spoiler regions
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">regions</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>區域名稱</td>
<td>Varchar</td>
<td>東區</td>
</tr>
</table>
9. :::spoiler counties
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">counties</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>縣市名稱</td>
<td>Varchar</td>
<td>台北市</td>
</tr>
</table>
10. :::spoiler districts
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">districts</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>行政區名稱</td>
<td>Varchar</td>
<td>士林區</td>
</tr>
</table>
11. :::spoiler stores
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">stores</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>name</td>
<td></td>
<td>Y</td>
<td>門市名稱</td>
<td>Varchar</td>
<td>新店光明</td>
</tr>
<tr>
<td>region</td>
<td>F</td>
<td>Y</td>
<td>區域</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>county</td>
<td>F</td>
<td>Y</td>
<td>縣市</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>district</td>
<td>F</td>
<td>Y</td>
<td>行政區</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>comebuy_id</td>
<td></td>
<td>Y</td>
<td>公司儲存的代號</td>
<td>Varchar</td>
<td>0045</td>
</tr>
</table>
12. :::spoiler tempSales
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">tempSales</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>store</td>
<td></td>
<td>Y</td>
<td>門市代號</td>
<td>Varchar</td>
<td>0045</td>
</tr>
<tr>
<td>time</td>
<td></td>
<td>Y</td>
<td>訂單時間</td>
<td>Timestamp</td>
<td>2023/07/04 11:25:00</td>
</tr>
<tr>
<td>drink</td>
<td></td>
<td>Y</td>
<td>飲料代號</td>
<td>Varchar</td>
<td>A020006</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar</td>
<td>少奶/少冰/無糖</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar</td>
<td>布丁/椰果</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
13. :::spoiler sales
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">Sales</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>store</td>
<td>F</td>
<td>Y</td>
<td>門市</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>time</td>
<td></td>
<td>Y</td>
<td>訂單時間</td>
<td>Timestamp</td>
<td>2023/07/04 11:25:00</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
14. :::spoiler aggregateSalesDay
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesDay</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>store</td>
<td>F</td>
<td>Y</td>
<td>門市</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
15. :::spoiler aggregateSalesDayDistrict
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesDayDistrict</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>district</td>
<td>F</td>
<td>Y</td>
<td>行政區</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
16. :::spoiler aggregateSalesDayCounty
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesDayCounty</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>county</td>
<td>F</td>
<td>Y</td>
<td>縣市</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
17. :::spoiler aggregateSalesDayRegion
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesDayRegion</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>region</td>
<td>F</td>
<td>Y</td>
<td>區域</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
18. :::spoiler aggregateSalesHour
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesHour</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>store</td>
<td>F</td>
<td>Y</td>
<td>門市</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>hour</td>
<td></td>
<td>Y</td>
<td>時間 <div>(12 表示12:00-12:59)</div> </td>
<td>Integer</td>
<td>12</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>N</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>Y</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
19. :::spoiler aggregateSalesHourDistrict
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesHourDistrict</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>district</td>
<td>F</td>
<td>Y</td>
<td>行政區</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>hour</td>
<td></td>
<td>Y</td>
<td>時間 <div>(12 表示12:00-12:59)</div> </td>
<td>Integer</td>
<td>12</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
20. :::spoiler aggregateSalesHourCounty
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesHourCounty</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>county</td>
<td>F</td>
<td>Y</td>
<td>縣市</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>hour</td>
<td></td>
<td>Y</td>
<td>時間 <div>(12 表示12:00-12:59)</div> </td>
<td>Integer</td>
<td>12</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
21. :::spoiler aggregateSalesHourRegion
<table border="0" style="text-align:center">
<tr>
<td colspan="6"><b style="font-size:20px">aggregateSalesHourRegion</b></td>
</tr>
<tr>
<th>Variable</th>
<th>Primary/Foreign</th>
<th>Not null</th>
<th>Description</th>
<th>Type</th>
<th>Example</th>
</tr>
<tr>
<td>id</td>
<td>P</td>
<td>Y</td>
<td>Serial number</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>region</td>
<td>F</td>
<td>Y</td>
<td>區域</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>date</td>
<td></td>
<td>Y</td>
<td>日期</td>
<td>Date</td>
<td>2023-04-30</td>
</tr>
<tr>
<td>hour</td>
<td></td>
<td>Y</td>
<td>時間 <div>(12 表示12:00-12:59)</div> </td>
<td>Integer</td>
<td>12</td>
</tr>
<tr>
<td>drink</td>
<td>F</td>
<td>Y</td>
<td>飲料</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>ice</td>
<td>F</td>
<td>N</td>
<td>冰塊</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>sweet</td>
<td>F</td>
<td>N</td>
<td>甜度</td>
<td>Integer</td>
<td>1</td>
</tr>
<tr>
<td>taste</td>
<td></td>
<td>N</td>
<td>口味</td>
<td>Varchar[]</td>
<td>[少奶, 去籽]</td>
</tr>
<tr>
<td>topping</td>
<td></td>
<td>N</td>
<td>加料</td>
<td>Varchar[]</td>
<td>[布丁, 椰果]</td>
</tr>
<tr>
<td>price</td>
<td></td>
<td>Y</td>
<td>訂單總價</td>
<td>Integer</td>
<td>55</td>
</tr>
<tr>
<td>amount</td>
<td></td>
<td>Y</td>
<td>杯數</td>
<td>Integer</td>
<td>2</td>
</tr>
</table>
### 2.3 注意事項:
與外接資料有關的資料表只有 tempSales,只要注意匯入的資料符合該表欄位即可,資料中需注意門市代號、飲品代號、冰塊、甜度需事先儲存在 stores、 drinks、 sweets、ices 表中,若不存在該筆交易會被移除,請確保需要的資料都有事先透過 sql 加入或是從網站頁面操作加入。詳細的匯入流程請參閱另一份[文件資料庫建立及匯入詳細說明](https://drive.google.com/file/d/1RbFoQBiQmXDRMl0d0i9iccFx1aN1a3G_/view?usp=drive_link)。</div>
## 3. 後端
本專案後端由以 python 建立的 fastapi 框架所撰寫,故下列說明皆為 python 語法及用語。後端共提供 6 支 API,以下分別敘述每一支的功能。
1. /data
- 請求方式:`get`
- 輸入說明:`None`
- 輸出說明:`dictionary`,按照各維度建立出的 `dictionary`, 詳見 [Schema](#Schema1)
- 功能說明:用於獲取各維度的數據。包含地區、飲品、甜度、冰塊、口味及加料。而地區與飲品會依照層級回傳以便前端呈現,如新店光明店會下屬於北區、新北市、新店區,而海神會下屬於原葉鮮萃茶類。
2. /search_item
- 請求方式:`post`
- 輸入說明:
```python
class SearchInput(BaseModel):
start_date: date=None # 搜尋日期的開始時間(e.g. 2023-01-23)
end_date: date=None # 搜尋日期的結束時間(e.g. 2023-01-26)
start_hour: int=None # 搜尋的開始小時(e.g. 10,自 10:00 開始的銷售數據)
end_hour: int=None # 搜尋的結束小時(e.g. 14,至 13:59 的銷售數據)
counties: List[str]=None # 欲比較之縣市層級(e.g. [新北市])則輸入,無需則不用
districts: List[str]=None # 欲比較之地方行政區層級(e.g. [新店區])則輸入,無需則不用
regions: List[str]=None # 欲比較之區域層級(e.g. [北區])則輸入,無需則不用
stores: List[str]=None # 欲比較之門市層級(e.g. [新店光明店])則輸入,無需則不用
drink: List[str]=None # 欲查詢之飲品名稱(e.g. [海神, 鮮萃大麥紅茶])
toppings: List[str]=None # 配料(e.g. [布丁, 椰果])
tastes: List[str]=None # 口味(e.g. [少奶, 去籽])
sweets: List[str]=None # 甜度(e.g. [正常糖, 少糖])
ices: List[str]=None #冰塊(e.g. [少冰, 去冰])
part: bool=None #是否需計算部分佔比
```
- 輸出說明:`Array[dictionary]`,`dictionary`為
```json
{
date:交易日期,
location:地區名稱,
drink:飲品名稱,
constraints:欲查詢之額外條件,
all:所有條件,
price:飲品總金額,
amount:飲品總數量,
total_price:地區總金額,
total_amount:地區總數量,
price_proportion:飲品金額佔比,
amount_proportion:飲品數量佔比,
}
```
- 功能說明:根據使用者所選擇的維度篩選對應的總表資料。將彙整後擁有不同額外條件的飲品數據除以全品項數據會得到佔比,用於得知每日特定條件飲品的銷售狀況。
3. /bar_item
- 請求方式:`post`
- 輸入說明:`int period`,欲查詢之期數
- 輸出說明:`Array[dictionary]`,`dictionary` 為
```json
{
location:地區名稱,
start_date:開始日期,
end_date:結束日期,
drink:飲品名稱,
constraint:額外條件,
price:飲品總金額,
amount:飲品總數量,
total_price:地區總金額,
total_amount:地區總數量,
price_proportion:飲品金額佔比,
amount_proportion:飲品數量佔比,
}
```
- 功能說明:根據輸入的期數以呈現若干時段的銷售趨勢。期數所劃分出的時間區段意義如下。假設查詢區段為 2023/01/01 至 2023/01/05 共 5 天,並選擇 3 期。上一期的時間區間則為 2022/12/27 至 2022/12/31,更上一期則為 2022/12/22 至 2022/ 12/26。 在不勾選分別飲品統整的條件下,數據計算方式為「特定條件的飲品數據 / 全品項數據」;而在勾選的狀況下,分母會變成「特定飲品數據」。舉例來說,假設春假期間海神在新店光明店共賣出 560 杯,其中去冰有 80 杯。而新店光明店在春假期間的總銷售杯數為 56000 杯。那麼前者則在計算「春假期間去冰海神在新店光明店的杯數占比」,即 $\dfrac{80}{56000}$;而後者代表「去冰海神在海神銷售中的杯數占比」,即 $\dfrac{80}{560}$。若並無查詢特定條件,那麼計算方式則為「特定飲品數據 / 全品項數據」,即「海神在所有銷售杯數中的佔比」,算式為 $\dfrac{560}{56000}$。
4. /line_item
- 請求方式:`post`
- 輸入說明:`int year`,欲查詢之年份總數
- 輸出說明:`Array[dictionary]`,`dictionary` 為
```json
{
location:地區名稱,
year:年份,
drink:飲品名稱,
constraint:額外條件,
price:飲品總金額,
amount:飲品總數量,
total_price:地區總金額,
total_amount:地區總數量,
price_proportion:飲品金額佔比,
amount_proportion:飲品數量佔比,
}
```
- 功能說明:根據輸入的年分以呈現若干年的銷售趨勢。假設查詢區段為 2023/01/01 至 2023/01/05 ,並選擇 3 年,會呈現 2021 至 2023 年在 01/01 到 01/05 的銷售數據,數據計算方式與期數銷售趨勢的計算方式相同。若期間有跨年資訊,則會將該筆數據歸類在後一年。舉例,2022/12/25 至 2023/01/02 的數據在圖上會被歸類在 2023 年,而 2021/12/25 至 2022/01/02 的數據則會被歸類在 2022 年。
5. /update
- 請求方式:`post`
- 輸入說明:
```python
class UpdateInput(BaseModel):
# 以下 list 若有輸入則須包含兩個元素
# 第一個是原始數據,第二個是欲更新之名稱,e.g. [北區, 北北區]
counties: List[str]=None
districts: List[str]=None
regions: List[str]=None
stores: List[str]=None
drinks: List[str]=None
categories: List[str]=None
toppings: List[str]=None
tastes: List[str]=None
sweets: List[str]=None
ices: List[str]=None
```
- 輸出說明:`{status: "success"}`
- 功能說明:用於更新資料庫中的各維度品項名稱。有鑑於公司會不定期更改門市或飲品名稱,因此更新的需求隨之誕生,更新方式即透過選擇既有名稱再輸入新名稱即可,唯注意需在匯入修改名稱後的新數據之前便在本系統進行更新,才不會造成往後的數據計算疏漏。
6. /new
- 請求方式:`post`
- 輸入說明:
```python
class CreateInput(BaseModel):
# 每一個 dictionary 會包含兩個 key, 分別是 value, new
# value 是欲新增的名稱, 是 Array 或單一物件, new 代表是否是新增的名稱, 是 bool
# e.g. toppings: {value: 新配料, new: true}, stores: {value: [新門市, comebuy_id], new: true}, regions: {value: 北區, new: false}
counties: dict=None
districts: dict=None
regions: dict=None
stores: dict=None
categories: dict=None
drinks: dict=None
toppings: dict=None
tastes: dict=None
sweets: dict=None
ices: dict=None
```
- 輸出說明:`{status: "success"}`
- 功能說明:用於新增各維度的品項。在新增門市與飲品前,須按照層級一步步輸入,如「北區、新北市、新店區」最後再輸入門市名稱「新店光光店」,日後才得以比較不同地區層級的銷售數據。而飲品則需要先輸入 飲品類別,如「原葉先萃茶」再輸入「海霸王」。如同更新所言,需在匯入 新資料前便在本平台進行新增,以確保後續數據計算的正確性。
## 4. 前端
前端由建於 javascript 上的 React 框架撰寫,主要程式碼位於 `frontend/src` 之下,以下將以 `src` 下一層之資料夾逐一說明功能
### 1. `components`:提供在專案內重複使用之 UI layout
#### `Drawer.js`:在視覺化分析頁面之滑抽及選項
:::spoiler **props**
#### <div style="font-weight:700">open</div>
如果為 `true`,顯示元件。
#### Type: `boolean`
---
#### <div style="font-weight:700">setOpen</div>
控制 [`open`](#open) prop 值。
#### Type: `React.Dispatch<React.SetStateAction<boolean>>`
---
#### <div style="font-weight:700">showType</div>
圖上顯示之數值類型。
#### Type: `'杯數' | '杯數佔比' | '金額' | '金額佔比'`
---
#### <div style="font-weight:700">setShowType</div>
控制 [`showType`](#showType) prop 值。
#### Type: `React.Dispatch<React.SetStateAction<string>>`
---
#### <div style="font-weight:700">graphType</div>
圖類。
#### Type: `'長條圖' | '折線圖'`
---
#### <div style="font-weight:700">setGraphType</div>
控制 [`graphType`](#graphType) prop 值。
#### Type: `React.Dispatch<React.SetStateAction<string>>`
---
#### <div style="font-weight:700">calType</div>
圖上顯示的最終結果之數值計算方法。
#### Type: `'分開計算' | '合併計算'`
---
#### <div style="font-weight:700">setCalType</div>
控制 [`calType`](#calType) prop 值。
#### Type: `React.Dispatch<React.SetStateAction<string>>`
---
#### <div style="font-weight:700">constraint</div>
圖上顯示的最終結果之限制的額外條件。
#### Type: `string` (the union of constraints in database)
---
#### <div style="font-weight:700">setConstraint</div>
控制 [`constraint`](#constraint) prop 值。
#### Type: `React.Dispatch<React.SetStateAction<string>>`
---
#### <div style="font-weight:700">constraints</div>
所有提供的額外條件選項。
#### Type: `Array[string (the union of constraints in database)]`
:::
---
#### `Graph.js`:視覺化分析之圖形,包含長條圖及折線圖
:::spoiler **props**
#### [graphType](#graphType)
---
#### <div style="font-weight:700">THEME</div>
圖上每個元素的代表顏色。
#### Type: `Array[string (hex color)]`
---
#### <div style="font-weight:700">handleLegendClick</div>
當各圖例被點擊時觸發的 Callback。
#### Type: `func`
---
#### <div style="font-weight:700">graphKey</div>
The render key. 改變時會觸發圖的動畫顯示。
#### Type: `number`
---
:::success
:art: **Graph Props**
#### 見更多 [recharts.js guideline](https://recharts.org/en-US/api).
* cartesianGridProps
* xAxisProps
* yAxisProps
* tooltipProps
* legendProps
:::
---
#### `GridToolBar.js`:表格上方之客製化工具列
:::spoiler **props**
#### <div style="font-weight:700">fileName</div>
匯出 excel 之檔案名稱。
#### Type: `string`
:::
---
### 2. `hooks`:提供 customed React hooks
#### `useCondition.js`:useContext 全局提供傳送到後端的資料群
:::spoiler **returns**
#### <div style="font-weight:700">condition</div>
傳送到後端的全局資料,包含時間、地點、飲品等等。
#### Schema:
```json
{
time: {
time: {
start: dayjs(dayjs().format().slice(0, 11) + "T00:00"),
end: dayjs(dayjs().format().slice(0, 11) + "T23:59"),
},
date: {
start: dayjs("2023-01-07"),
end: dayjs("2023-01-07"),
},
},
location: [
{
name: "新店光明店",
level: "store",
route: ["北區", "新北市", "新店區"],
},
{
name: "花蓮中山店",
level: "store",
route: ["東區", "花蓮縣", "花蓮市"],
},
],
beverage: [
{ name: "海神", category: "原葉鮮萃茶" },
{ name: "鮮萃大麥紅茶", category: "原葉鮮萃茶" },
],
ice: [],
sweet: [],
flavor: [],
topping: [],
part: false,
}
```
---
#### <div style="font-weight:700">DATA</div>
全局資料,用於顯示主頁的篩選選項。
#### Schema:
```json
{
"全台": {
"北區": {
"新北市": {
"新店區": [
"新店光明店"
]
}
},
"東區": {
"花蓮縣": {
"花蓮市": [
"花蓮中山店"
]
}
}
},
"飲品": {
"原葉鮮萃茶": [
"海神",
"鮮萃大麥紅茶"
]
},
"甜度": ["半蜜", "多糖", "微糖", "少糖", "正常糖", "標準糖", "微蜜", "多蜜", "半糖"...],
"冰塊": ["熱品", "正常冰", "微冰", "少冰", "溫飲", "去冰", "標準冰", "熱飲", "溫品"...],
"加料": ["紫米", "蘆薈", "搖果樂", "愛玉", "小紫蘇", "荔枝凍", "新雙Q", "雙Q條"...],
"口味": ["少奶", "去籽", "多籽", "紅茶", "換奶綠", "多奶", "改小珍", "改鬥陣", "芋"...]
}
```
---
#### <div style="font-weight:700">systemState</div>
目前系統狀況, 包含「地區級別」和「是否為個別佔比計算」。每一次送出 request 都會更新。
#### Schema:
```json
{
"locationLevel": "region",
"part": false
}
```
---
#### <div style="font-weight:700">setSystemState</div>
控制 `systemState` 值。
#### Type: `React.Dispatch<React.SetStateAction<{}>>`
---
#### <div style="font-weight:700">setCondition</div>
控制 `condition` 值。
#### Type: `React.Dispatch<React.SetStateAction<{}>>`
---
#### <div style="font-weight:700">setDATA</div>
控制 `DATA` 值。
#### Type: `React.Dispatch<React.SetStateAction<{}>>`
---
#### <div style="font-weight:700">loading</div>
如果為 `true`,會顯示加載元件。在送出 request 至接收 response 以前為 `true`
#### Type: `boolean`
---
#### <div style="font-weight:700">setLoading</div>
控制 `loading` 值。
#### Type: `React.Dispatch<React.SetStateAction<boolean>>`
---
#### <div style="font-weight:700">addLocationCondtion</div>
當未被選取之地區被選取時觸發的 Callback。
#### Type: `func`
---
#### <div style="font-weight:700">deleteLocationCondition</div>
當被選取之地區被選取時觸發的 Callback。
#### Type: `func`
---
#### <div style="font-weight:700">addBeverageCondition</div>
當未被選取之飲品被選取時觸發的 Callback。
#### Type: `func`
---
#### <div style="font-weight:700">deleteBeverageCondition</div>
當被選取之飲品被選取時觸發的 Callback。
:::
---
#### `useGraph.js`:提供視覺化所需 states 及 effects
:::spoiler **returns**
#### <div style="font-weight:700">period</div>
圖上顯示的時間區間數。
#### Type: `number`
---
#### [showType](#showType)
---
#### [setShowType](#setShowType)
---
#### [graphType](#graphType)
---
#### [setGraphType](#setGraphType)
---
#### [calType](#calType)
---
#### [setCalType](#setCalType)
---
#### [constraint](#constraint)
---
#### [setConstraint](#setConstraint)
---
#### <div style="font-weight:700">points</div>
圖的資料點元素。
#### Schema:
```json
{
data: [
{
year: "2023-01-05 2023-01-05",
"新店光明 海神": 48.04,
"新店光明 鮮萃大麥紅茶": 51.96,
"花蓮中山 海神": 51.85,
"花蓮中山 鮮萃大麥紅茶": 48.15
},
{
year: "2023-01-06 2023-01-06",
"新店光明 海神": 54.2,
"新店光明 鮮萃大麥紅茶": 45.8,
"花蓮中山 海神": 51.49,
"花蓮中山 鮮萃大麥紅茶": 48.51
},
{
year: "2023-01-07 2023-01-07",
"新店光明 海神": 50,
"新店光明 鮮萃大麥紅茶": 50,
"花蓮中山 海神": 50.44,
"花蓮中山 鮮萃大麥紅茶": 49.56
}
],
point: [
{ name: "新店光明 海神", hide: false },
{ name: "新店光明 鮮萃大麥紅茶", hide: false},
{ name: "花蓮中山 海神", hide: false },
{ name: "花蓮中山 鮮萃大麥紅茶", hide: false }
]
}
```
---
#### [key](#graphKey)
---
#### <div style="font-weight:700">handlePeriodChange</div>
當 [period](#period) 改變時觸發的 Callback
#### Type: `func`
---
#### [handleLegendClick](#handleLegendClick)
---
:::success
:star: **Effects**
#### 1. 當任何圖類相關變數改變時重建構 [`points`](#points) 值。
```javascript=111
useEffect(() => {
setPoints(dataFormatProcessing(rawData, period, showType, constraint));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [rawData, showType, constraint, dataFormatProcessing]);
```
#### 2. 當任何圖類相關變數改變時改變 [`key`](#graphKey) 值<text style="color:#c62828">以觸發內建動畫</text>。
```javascript=140
useEffect(() => {
setKey((prev) => (prev % 10) + 1);
}, [showType, constraint, rawData, graphType, calType]);
```
:::
---
### 3. `Manipulate`:`/manipluate` 頁面
#### `Add.js`:服務「新增」頁面
#### `constant.js`:各式 mapping object
#### `index.js`:`/manipluate` 彙整頁面,包含 Tab Pages
#### `Rename.js`:服務「修改」頁面
### 4. `middleware`:提供 Data Fetching function
#### `axios.js`:
* create general instance for single endpoint base.
* provide interceptor during data fetching.
#### `const.js`:provide function to fetch `GET /data`.
#### `index.js`:provide collection of all data-fetching function.
#### `simplePost.js`:provide functions to fetch `post`-related endpoints.
* `POST /search_item`
* `POST /line_item`
* `POST /bar_item`
* `POST /new`
* `POST /update`
### 5. `Options`:提供主頁(搜尋頁)的各式選項
#### `assets`:Every images and GIFs used in Options.
#### `Beverage`:飲品選項
#### `Ice`:冰塊選項
#### `Location`:地區選項
* **`Modal.js`:地區的彈出視窗**
* **`Recursive_Component.js`:遞迴的 components shown in Modal.js**
#### `Sweet`:甜度選項
#### `Time`:時間選項
#### `Flavor.js`:口味選項
#### `SieveType`:彙整所有選項
#### `Topping`:加料選項
### 6. `utils`:專案內使用的工具
#### `day.js`:提供全局 dayjs 的額外配置(e.g. 設置 sameElse,不使用 dayjs 內建顯示時間)
### 7. `Visualization`:視覺化分析
#### `Aggregate.js`:提供「數據統計」表
:::spoiler **props**
#### <div style="font-weight:700">data</div>
表格的資料。
#### Schema:
```json
[
{
location: "新店光明",
start_date: "2023-01-07",
end_date: "2023-01-07",
drink: "海神",
price: 3275,
amount: 57,
total_price: 6465,
total_amount: 114,
price_proportion: 50.66,
amount_proportion: 50
}...
]
```
:::
---
#### `index.js`:彙整所有視覺化之圖表
#### `PeriodAnalysis.js`:提供「過去若干時段趨勢」分析圖
:::spoiler **props**
#### <div style="font-weight:700">data</div>
圖上元素依賴的資料。
#### [Schema](#Schema4)
---
#### [THEME](#THEME)
---
#### <div style="font-weight:700">setTheme</div>
控制 `THEME` prop 值。
#### Type: `React.Dispatch<React.SetStateAction<[]>>`
:::
---
#### `StoreBeverage.js`:提供「總表」,是後臺資料庫的原始數據
:::spoiler **props**
#### <div style="font-weight:700">data</div>
表格的資料。
#### Schema:
```json
[
{
date: "2023-01-07",
location: "新店光明",
drink: "海神",
price: 435,
amount: 9,
total_price: 6465,
total_amount: 114,
price_proportion: 0.0673,
amount_proportion: 0.0789,
constraints: [],
all: ["正常冰", "半糖", "椰果"]
}...
]
```
:::
---
#### `YearAnalysis.js`:提供「過去若干年趨勢」分析圖
:::spoiler **props**
#### <div style="font-weight:700">data</div>
圖上元素依賴的資料。
#### Schema:
```json
[
{
location: "新店光明",
drink: "海神",
year: 2021,
price: 3535,
amount: 62,
total_price: 6655,
total_amount: 117,
price_proportion: 53.12,
amount_proportion: 52.99
}...
]
```
---
#### [THEME](#THEME)
---
#### [setTheme](#setTheme)
:::