---
# System prepended metadata

title: 第 5-8 週 JavaScript 作業設計

---

# 第 5-8 週 JavaScript 作業設計

> 後端導向 JavaScript 基礎課程，銜接 4-6 月 Node.js 課程
>
> **技術環境：Node.js + 原生 JavaScript**
>
> **作業情境：電商購物車系統**

---

## 作業設計規劃

### 設計原則

1. **後端思維導向**：作業設計著重資料處理、API 串接、邏輯拆分
2. **銜接 Node.js**：使用電商系統常見的資料結構與業務邏輯
3. **循序漸進**：從純邏輯處理 → 非同步操作 → 套件使用 → 專案整合
4. **實務情境**：模擬真實後端會遇到的資料處理場景
5. **測試驅動**：每週作業皆有 Jest 測試，培養測試習慣

### 為什麼用電商系統？

電商系統涵蓋後端開發最核心的概念：

- **產品模組**：資料查詢、篩選、分類
- **購物車模組**：狀態管理、數量計算、CRUD 操作
- **訂單模組**：資料驗證、流程整合、統計報表
- **使用者驗證**：表單驗證、認證機制

這些概念在 Node.js 課程中會直接延續，學員可以從「呼叫 API」變成「建立 API」。

### 技術選型

| 項目 | 選擇 | 原因 |
|------|------|------|
| 執行環境 | Node.js 18+ | 內建 fetch，與後端環境一致 |
| 測試框架 | Jest | 業界主流，語法直覺 |
| HTTP 請求 | fetch | 原生 API，銜接後端課程再學 axios |
| 日期處理 | dayjs | 輕量、API 直覺 |
| API 來源 | LiveJS 電商 API | 六角學院提供，穩定可靠 |

### 學習路線圖

```
第五週          第六週          第七週          第八週
────────────────────────────────────────────────────────
純邏輯處理  →  非同步操作  →  第三方套件  →  專案整合

函式拆分       fetch API      dayjs          模組化架構
資料處理       Promise        fetch 進階      服務層設計
陣列方法       async/await    資料驗證       完整 CRUD
```

---

## 給助教的說明

### 測試方式差異

| 週次 | 測試方式 | 執行指令 | 原因 |
|------|----------|----------|------|
| 第五週 | 原生 JS 測試 | `node test.js` | 純邏輯練習，不需安裝套件 |
| 第六～八週 | Jest | `npm test` | 開始接觸 npm、非同步測試 |

### 為什麼第五週不用 Jest？

1. **降低入門門檻**：第五週專注在 JS 邏輯，不需學 npm、套件安裝
2. **快速驗證**：學員只要執行 `node test.js` 就能看到結果
3. **循序漸進**：第六週開始用 npm，再引入 Jest 測試
4. **理解測試原理**：用原生 JS 寫測試框架，學員更能理解測試的運作方式

### 第五週如何驗收

```bash
# 執行測試檔案
node test.js
```

測試結果會顯示通過/失敗數量，例如：

```text
========== 任務一：產品查詢模組 ==========

✓ getProductById - 找到產品應回傳物件
✓ getProductById - 找不到應回傳 null
✓ getProductsByCategory - 應回傳陣列
...

==========================================
測試結果: 28 通過, 0 失敗, 共 28 項
==========================================

🎉 太棒了！所有測試都通過了！
```

**驗收重點**：
- 測試通過數量
- 是否有修改原陣列（任務三要求不修改原陣列）

### 第六～八週為什麼用 Jest？

1. **客觀評分**：測試通過數量可直接對應分數，減少主觀判斷
2. **即時回饋**：學員可自行執行 `npm test` 確認完成度
3. **培養習慣**：Node.js 課程會大量使用測試，提前熟悉
4. **非同步測試**：Jest 支援 async/await 測試，適合 API 串接

### 第六～八週如何驗收

```bash
# 1. clone 學員的 repo
git clone <學員repo網址>

# 2. 進入對應週次資料夾
cd week6/assignment

# 3. 安裝套件
npm install

# 4. 執行測試
npm test
```

測試結果會顯示通過/失敗數量，例如：

```text
Tests:       18 passed, 3 failed, 21 total
```

**評分建議**：通過率 × 該週配分 = 得分

### 每週作業設計目的

| 週次 | 設計目的 | 驗收重點 |
|------|----------|----------|
| 第五週 | 練習函式拆分、陣列方法 | 純邏輯正確、不修改原陣列 |
| 第六週 | 學會 fetch、async/await | API 串接成功、錯誤處理 |
| 第七週 | 熟悉第三方套件使用 | dayjs 正確使用、fetch 進階 |
| 第八週 | 整合能力、模組化思維 | 服務層架構、程式組織 |

### 難度調整建議

如果學員普遍卡關，可考慮以下調整：

- **第五週**：任務四（訂單統計）可改為選做
- **第六週**：任務四（管理員 API）可改為加分題
- **第七週**：任務五（OrderService 整合）可改為加分題
- **第八週**：後台訂單管理可設為加分題

### 常見問題排解

| 問題 | 解決方式 |
|------|----------|
| `.env` 找不到 | 確認 `.env` 在專案根目錄（2025-js-plan/） |
| API 請求失敗 | 確認 `API_PATH` 和 `API_KEY` 正確 |
| Jest 超時 | 網路問題，可調高 `jest.setTimeout()` |
| 模組找不到 | 執行 `npm install` 安裝相依套件 |

---

## 使用的 API

本課程使用**六角學院 LiveJS 電商 API**：

- API 文件：https://hexschool.github.io/hexschoolliveswagger/
- 學員需先申請自己的 `api_path` 和 `api_key`

---

## 課程定位

| JS 週次 | 學習重點 | 銜接 Node.js |
|---------|----------|--------------|
| 第五週 | 函式拆分、邏輯整合 | Express MVC、Controller |
| 第六週 | Promise、fetch、async/await | 非同步資料庫操作 |
| 第七週 | 第三方套件（dayjs）、fetch 進階 | npm 生態系 |
| 第八週 | RESTful API 整合 | 自建 RESTful API |

---

## 第五週：JavaScript 模組化開發

### 作業主題
電商資料處理系統（純邏輯，不串 API）

### 任務清單

| 任務 | 配分 | 內容 |
|------|------|------|
| 任務一 | 20% | 產品查詢模組 |
| 任務二 | 25% | 購物車計算模組 |
| 任務三 | 30% | 購物車操作模組 |
| 任務四 | 25% | 訂單統計模組 |

### 需實作函式

**任務一：產品查詢**
- `getProductById(products, productId)` - 根據 ID 查詢產品
- `getProductsByCategory(products, category)` - 根據分類篩選
- `getDiscountRate(product)` - 計算折扣率（如 "8折"）
- `getAllCategories(products)` - 取得不重複分類

**任務二：購物車計算**
- `calculateCartOriginalTotal(carts)` - 原價總金額
- `calculateCartTotal(carts)` - 售價總金額
- `calculateSavings(carts)` - 省下金額
- `calculateCartItemCount(carts)` - 商品總數量
- `isProductInCart(carts, productId)` - 檢查是否在購物車

**任務三：購物車操作**
- `addToCart(carts, product, quantity)` - 新增商品
- `updateCartItemQuantity(carts, cartId, newQuantity)` - 更新數量
- `removeFromCart(carts, cartId)` - 移除商品
- `clearCart()` - 清空購物車

**任務四：訂單統計**
- `calculateTotalRevenue(orders)` - 已付款訂單營收
- `filterOrdersByStatus(orders, isPaid)` - 篩選訂單狀態
- `generateOrderReport(orders)` - 產生統計報表
- `groupOrdersByPayment(orders)` - 依付款方式分組

---

## 第六週：非同步程式開發

### 作業主題
電商 API 資料串接（fetch + async/await）

### 任務清單

| 任務 | 配分 | 內容 |
|------|------|------|
| 任務一 | 20% | 基礎 fetch 練習 |
| 任務二 | 30% | POST/PATCH/DELETE 請求 |
| 任務三 | 30% | Promise 處理與整合 |
| 任務四 | 15% | 管理員 API（需認證） |
| 加分題 | 5% | HTTP 知識測驗 |

### 需實作函式

**任務一：基礎 GET 請求**
- `getProducts()` - 取得產品列表
- `getCart()` - 取得購物車
- `getProductsSafe()` - 含錯誤處理

**任務二：購物車操作**
- `addToCart(productId, quantity)` - 加入購物車 (POST)
- `updateCartItem(cartId, quantity)` - 更新數量 (PATCH)
- `removeCartItem(cartId)` - 刪除商品 (DELETE)
- `clearCart()` - 清空購物車 (DELETE)

**任務三：Promise 整合**
- `getProductsAndCart()` - 同時取得產品和購物車 (Promise.all)
- `addMultipleToCart(items)` - 批次加入購物車
- `checkout(userInfo)` - 完整結帳流程

**任務四：管理員 API**
- `getOrders()` - 取得訂單（需認證）
- `updateOrderStatus(orderId, isPaid)` - 更新訂單狀態
- `deleteOrder(orderId)` - 刪除訂單

---

## 第七週：第三方套件整合

### 作業主題
使用 dayjs 優化電商系統（延續 fetch 練習）

### 套件選用分析

第七週是學員第一次使用 `npm install` 安裝外部套件，選用原則如下：

| 任務 | 建議方式 | 原因 |
|------|----------|------|
| 日期處理 | ✅ **dayjs** | 原生寫法太繁瑣，dayjs 能明顯簡化 |
| 資料驗證 | ✅ **原生 JS** | 規則簡單，用 if/正則即可，不需套件 |
| ID 產生 | ✅ **原生 JS** | `Date.now() + Math.random()` 就夠用 |
| API 串接 | ✅ **fetch** | 學員需先熟悉原生，axios 留給 Node.js 課程 |

### 為什麼用 dayjs？

任務一的 4 個函式都需要日期處理，原生寫法 vs dayjs 差異明顯：

```javascript
// === formatOrderDate ===

// 原生 JS - 繁瑣
function formatOrderDate(timestamp) {
  const d = new Date(timestamp * 1000);
  const year = d.getFullYear();
  const month = String(d.getMonth() + 1).padStart(2, '0');
  const day = String(d.getDate()).padStart(2, '0');
  const hour = String(d.getHours()).padStart(2, '0');
  const min = String(d.getMinutes()).padStart(2, '0');
  return `${year}/${month}/${day} ${hour}:${min}`;
}

// dayjs - 一行搞定
function formatOrderDate(timestamp) {
  return dayjs.unix(timestamp).format('YYYY/MM/DD HH:mm');
}
```

```javascript
// === getThisWeekOrders ===

// 原生 JS - 要自己算週的開始結束
function getThisWeekOrders(orders) {
  const now = new Date();
  const dayOfWeek = now.getDay();
  const startOfWeek = new Date(now);
  startOfWeek.setDate(now.getDate() - dayOfWeek);
  startOfWeek.setHours(0, 0, 0, 0);
  // ... 還要算 endOfWeek，很繁瑣 ...
}

// dayjs - 直覺
function getThisWeekOrders(orders) {
  const startOfWeek = dayjs().startOf('week');
  const endOfWeek = dayjs().endOf('week');
  return orders.filter(order => {
    const orderDate = dayjs.unix(order.createdAt);
    return orderDate.isAfter(startOfWeek) && orderDate.isBefore(endOfWeek);
  });
}
```

### 為什麼不用 axios？

學員應該先熟悉原生 fetch，再學 axios：

```
第六週：fetch 基礎（GET/POST/PATCH/DELETE）
第七週：fetch 進階 + dayjs（錯誤處理、日期格式化）
第八週：fetch 整合（模組化架構）
Node.js：axios（後端課程再學）
```

### 為什麼資料驗證和 ID 產生用原生？

1. **資料驗證**：規則簡單（正則表達式 + if 判斷），不需要 validator 套件
2. **ID 產生**：`Date.now().toString(36) + Math.random().toString(36)` 就夠用，不需要 uuid 套件

這樣學員能學到：
- 原生 JS 能力（正則、隨機數）
- 判斷「什麼時候該用套件」的思維

### 任務清單

| 任務 | 配分 | 內容 |
|------|------|------|
| 任務一 | 25% | dayjs 日期處理 |
| 任務二 | 20% | 資料驗證（原生 JS） |
| 任務三 | 10% | ID 產生（原生 JS） |
| 任務四 | 20% | fetch API 進階 |
| 任務五 | 25% | OrderService 整合 |

### 需實作函式

**任務一：dayjs 日期處理**
- `formatOrderDate(timestamp)` - 轉為 "YYYY/MM/DD HH:mm"
- `getDaysAgo(timestamp)` - 計算距今幾天
- `isOrderOverdue(timestamp)` - 判斷是否超過 7 天
- `getThisWeekOrders(orders)` - 篩選本週訂單

**任務二：資料驗證（原生 JS）**
- `validateOrderUser(data)` - 驗證訂單使用者資料
- `validateCartQuantity(quantity)` - 驗證數量（1-99）

**任務三：ID 產生（原生 JS）**
- `generateOrderId()` - 格式 "ORD-xxxxxxxx"
- `generateCartItemId()` - 格式 "CART-xxxxxxxx"

**任務四：fetch API 進階**
- `getProducts()` - 取得產品（含錯誤處理）
- `addToCart(productId, quantity)` - 加入購物車
- `getOrders()` - 取得訂單（需認證）

**任務五：OrderService 整合**
```javascript
const OrderService = {
  fetchOrders(),      // fetch 取得訂單
  formatOrders(),     // dayjs 格式化日期
  filterUnpaidOrders(), // 篩選未付款
  validateUserInfo(), // 驗證使用者
  getUnpaidOrdersFormatted() // 整合方法
}
```

---

## 第八週：期末專案

### 作業主題
電商系統整合（模組化架構）

### 任務清單

| 任務 | 配分 | 內容 |
|------|------|------|
| API 模組 | 25% | api.js |
| 工具函式 | 15% | utils.js |
| 產品服務 | 20% | productService.js |
| 購物車服務 | 25% | cartService.js |
| 訂單服務 | 15% | orderService.js |

### 專案架構

```
week8/assignment/
├── config.js           # API 設定
├── api.js              # API 請求函式
├── utils.js            # 工具函式
├── services/
│   ├── productService.js
│   ├── cartService.js
│   └── orderService.js
├── app.js              # 主程式入口
└── test.js             # Jest 測試
```

### 各檔案需實作函式

**api.js（10 個函式）**
- 客戶端：fetchProducts, fetchCart, addToCart, updateCartItem, deleteCartItem, clearCart, createOrder
- 管理員：fetchOrders, updateOrderStatus, deleteOrder

**utils.js（6 個函式）**
- getDiscountRate, getAllCategories, formatDate, getDaysAgo, validateOrderUser, validateCartQuantity

**productService.js（5 個函式）**
- getProducts, getProductsByCategory, getProductById, getCategories, displayProducts

**cartService.js（7 個函式）**
- getCart, addProductToCart, updateProduct, removeProduct, emptyCart, getCartTotal, displayCart

**orderService.js（8 個函式）**
- placeOrder, getOrders, getUnpaidOrders, getPaidOrders, updatePaymentStatus, removeOrder, formatOrder, displayOrders

---

## API 端點參考

```
Base URL: https://livejs-api.hexschool.io

=== 客戶端 API ===
GET    /api/livejs/v1/customer/{api_path}/products     取得產品
GET    /api/livejs/v1/customer/{api_path}/carts        取得購物車
POST   /api/livejs/v1/customer/{api_path}/carts        加入購物車
PATCH  /api/livejs/v1/customer/{api_path}/carts        更新數量
DELETE /api/livejs/v1/customer/{api_path}/carts        清空購物車
DELETE /api/livejs/v1/customer/{api_path}/carts/{id}   刪除商品
POST   /api/livejs/v1/customer/{api_path}/orders       建立訂單

=== 管理員 API（需 Token）===
GET    /api/livejs/v1/admin/{api_path}/orders          取得訂單
PUT    /api/livejs/v1/admin/{api_path}/orders          更新訂單
DELETE /api/livejs/v1/admin/{api_path}/orders/{id}     刪除訂單
```

---

## 驗證規則參考

| 欄位 | 規則 |
|------|------|
| name | 不可為空 |
| tel | 09 開頭的 10 位數字 |
| email | 包含 @ 符號 |
| address | 不可為空 |
| payment | ATM / Credit Card / Apple Pay |
| quantity | 正整數，1-99 |

---

## 測試指令

每週作業都使用 Jest 測試：

```bash
# 進入該週資料夾
cd week6/assignment

# 安裝套件
npm install

# 執行測試
npm test
```

---

## 繳交方式

1. 完成作業檔案中的所有函式
2. 執行 `npm test` 確保測試通過
3. 上傳至 GitHub
4. 提交 GitHub 連結
