跳至主要内容

US-103: 商品照片管理

User Story

作為 花店管理者 我想要 能夠上傳、管理和排序商品照片 以便 為商品提供視覺展示,提升客戶購買意願


驗收標準 (Acceptance Criteria)

Scenario 1: 上傳商品主圖

  • Given 我是已登入的管理者
  • And 我在「新增商品」或「編輯商品」頁面
  • When 我點擊主圖上傳區域
  • And 我選擇一張 JPG 格式、大小 2MB 的照片
  • Then 系統應顯示上傳進度
  • And 上傳完成後應顯示照片預覽
  • And 系統應自動生成縮圖版本
  • And 系統應自動轉換為 WebP 格式

Scenario 2: 拖曳上傳主圖

  • Given 我是已登入的管理者
  • And 我在商品表單頁面
  • When 我將一張照片檔案拖曳到主圖上傳區域
  • Then 拖曳區域應顯示視覺提示(邊框變色)
  • And 放開後應開始上傳
  • And 上傳完成後應顯示照片預覽

Scenario 3: 上傳多張副圖

  • Given 我是已登入的管理者
  • And 我在商品表單頁面
  • And 商品已有主圖
  • When 我點擊「新增副圖」按鈕
  • And 我一次選擇 3 張照片
  • Then 系統應顯示每張照片的上傳進度
  • And 上傳完成後應在副圖區域顯示所有照片
  • And 照片應按上傳順序排列

Scenario 4: 拖曳排序副圖

  • Given 我是已登入的管理者
  • And 商品已有 4 張副圖
  • When 我拖曳第 4 張照片到第 1 張的位置
  • Then 照片順序應更新為:原第4張、原第1張、原第2張、原第3張
  • And 排序變更應即時儲存

Scenario 5: 設定副圖為主圖

  • Given 我是已登入的管理者
  • And 商品已有 1 張主圖和 3 張副圖
  • When 我點擊第 2 張副圖的「設為主圖」按鈕
  • Then 原本的主圖應移動到副圖區域的最前面
  • And 第 2 張副圖應成為新的主圖

Scenario 6: 刪除副圖

  • Given 我是已登入的管理者
  • And 商品已有 1 張主圖和 3 張副圖
  • When 我點擊第 1 張副圖的「刪除」按鈕
  • And 系統要求我確認
  • And 我點擊「確認刪除」
  • Then 該副圖應被移除
  • And 剩餘副圖應重新排序
  • And 雲端儲存的照片檔案也應被刪除

Scenario 7: 更換主圖

  • Given 我是已登入的管理者
  • And 商品已有主圖
  • When 我點擊主圖的「更換」按鈕
  • And 我選擇一張新照片
  • Then 新照片應替換原本的主圖
  • And 原本的主圖應被刪除

Scenario 8: 照片大小超過限制(錯誤場景)

  • Given 我是已登入的管理者
  • And 我在商品表單頁面
  • When 我嘗試上傳一張 15MB 的照片
  • Then 系統應顯示錯誤訊息「照片大小不能超過 10MB」
  • And 照片不應被上傳

Scenario 9: 照片格式不支援(錯誤場景)

  • Given 我是已登入的管理者
  • And 我在商品表單頁面
  • When 我嘗試上傳一張 GIF 格式的檔案
  • Then 系統應顯示錯誤訊息「僅支援 JPEG、PNG、WebP 格式」
  • And 檔案不應被上傳

Scenario 10: 副圖數量超過限制(錯誤場景)

  • Given 我是已登入的管理者
  • And 商品已有 5 張副圖(已達上限)
  • When 我嘗試新增第 6 張副圖
  • Then 系統應顯示錯誤訊息「副圖最多 5 張,請先刪除現有照片」
  • And 新照片不應被上傳

Scenario 11: 上傳過程中網路中斷(錯誤場景)

  • Given 我是已登入的管理者
  • And 我正在上傳一張照片
  • When 上傳過程中網路連線中斷
  • Then 系統應顯示錯誤訊息「上傳失敗,請檢查網路連線後重試」
  • And 應顯示「重試」按鈕

Scenario 12: 預覽商品照片

  • Given 我是已登入的員工
  • And 我在商品詳情頁
  • And 商品有 1 張主圖和 3 張副圖
  • When 我點擊主圖
  • Then 系統應開啟照片預覽 Modal(Lightbox)
  • And 應顯示高解析度版本
  • And 可左右滑動/點擊切換照片
  • And 點擊背景或關閉按鈕可關閉預覽

業務規則 (Business Rules)

  1. 照片數量限制

    • 主圖: 必須有且只有 1 張
    • 副圖: 最少 0 張,最多 5 張
    • 總計: 最多 6 張照片/商品
  2. 照片格式規範

    • 支援格式: JPEG (.jpg, .jpeg)、PNG (.png)、WebP (.webp)
    • 不支援: GIF、BMP、TIFF、RAW 等格式
    • 上傳後自動轉換為 WebP 格式(保留原檔備份)
  3. 照片大小規範

    • 單張最大: 10MB
    • 建議尺寸: 800x800px 以上
    • 最小尺寸: 200x200px
    • 最大尺寸: 4096x4096px
  4. 自動處理

    • 縮圖生成: 自動生成 200x200px 縮圖(用於列表顯示)
    • 壓縮版本: 自動生成 Web 顯示用壓縮版本(品質 85%)
    • 格式轉換: 自動轉換為 WebP 格式(減少 25-35% 體積)
    • EXIF 清除: 清除照片中的隱私資訊(GPS 等)
  5. 儲存路徑規則

    /{bucket}/tenant-{tenantId}/products/{productId}/
    ├── main.webp # 主圖(壓縮版)
    ├── main_original.jpg # 主圖(原檔)
    ├── main_thumb.webp # 主圖(縮圖)
    ├── gallery_1.webp # 副圖 1(壓縮版)
    ├── gallery_1_original.jpg # 副圖 1(原檔)
    ├── gallery_1_thumb.webp # 副圖 1(縮圖)
    └── ...
  6. 排序規則

    • 副圖順序由用戶自訂(拖曳排序)
    • 排序變更即時儲存
    • 順序用於商品詳情頁展示
  7. 刪除規則

    • 刪除副圖時同時刪除雲端儲存的所有版本(原檔、壓縮版、縮圖)
    • 主圖不可直接刪除,只能更換
    • 商品刪除時一併刪除所有照片
  8. 權限規則

    • ROLE_MANAGER 以上可上傳/刪除照片
    • ROLE_STAFF 可查看照片但無法修改
    • 所有用戶可預覽照片(Lightbox)
  9. 多租戶隔離

    • 照片 URL 包含 tenantId
    • 非授權訪問其他租戶照片返回 403
    • CDN 快取鍵包含 tenantId

UI/UX 需求 (UI/UX Requirements)

主圖上傳區域

┌─────────────────────────────────┐
│ │
│ [點擊上傳或拖曳照片到此] │
│ │
│ 支援 JPG、PNG、WebP │
│ 最大 10MB │
│ │
└─────────────────────────────────┘

上傳後狀態:

┌─────────────────────────────────┐
│ │
│ [照片預覽] │
│ │
├─────────────────────────────────┤
│ [更換] [預覽] │
└─────────────────────────────────┘

副圖管理區域

副圖(最多 5 張)

┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐ ┌────┐
│ 1 │ │ 2 │ │ 3 │ │ + │ │ │ │ │
│[圖]│ │[圖]│ │[圖]│ │新增│ │ │ │ │
│ × │ │ × │ │ × │ │ │ │ │ │ │
└────┘ └────┘ └────┘ └────┘ └────┘ └────┘
↔ 可拖曳排序

每張副圖的操作:

  • Hover 顯示操作按鈕
  • 「×」刪除按鈕(右上角)
  • 「⭐」設為主圖按鈕
  • 拖曳手把圖示

上傳進度顯示

┌─────────────────────────────────┐
│ filename.jpg │
│ ████████████░░░░░░░░ 65% │
│ 上傳中... │
└─────────────────────────────────┘
┌───────────────────────────────────────────────┐
│ [×] │
│ │
│ [<] [高解析度照片] [>] │
│ │
│ │
│ ○ ○ ● ○ ○ (分頁指示器) │
└───────────────────────────────────────────────┘

互動行為

  • 拖曳上傳:

    • 拖曳檔案到區域時,邊框變為藍色虛線
    • 區域文字變為「放開以上傳」
    • 不支援的檔案類型時,邊框變為紅色
  • 多檔上傳:

    • 同時上傳多張時,顯示每張的進度
    • 任一張失敗不影響其他張
    • 全部完成後顯示總結訊息
  • 拖曳排序:

    • 長按/點擊拖曳手把開始拖曳
    • 拖曳中顯示半透明預覽
    • 放置位置顯示藍色插入線
    • 放開後即時更新順序
  • 預覽 Lightbox:

    • 支援左右方向鍵切換
    • 支援 Esc 鍵關閉
    • 支援觸控滑動(手機)
    • 縮放功能(雙擊/捏合)

錯誤狀態

  • 檔案過大: 紅色提示框「照片大小不能超過 10MB」
  • 格式錯誤: 紅色提示框「僅支援 JPEG、PNG、WebP 格式」
  • 上傳失敗: 顯示錯誤圖示 + 「重試」按鈕
  • 數量超限: 紅色提示框「副圖最多 5 張」

響應式設計

  • 桌面: 6 欄網格顯示副圖
  • 平板: 4 欄網格顯示副圖
  • 手機: 3 欄網格顯示副圖,Lightbox 全螢幕

技術規格 (Technical Specifications)

API 端點

1. 上傳照片

  • 端點: POST /api/v1/products/upload-image
  • 權限要求: ROLE_MANAGER 或更高
  • Content-Type: multipart/form-data

請求:

POST /api/v1/products/upload-image
Content-Type: multipart/form-data

file: (binary)
productId: prod-abc123 (optional, for existing product)
imageType: main | gallery

響應 Payload:

{
"id": "img-xyz789",
"url": "https://cdn.example.com/tenant-001/products/prod-abc123/main.webp",
"thumbnailUrl": "https://cdn.example.com/tenant-001/products/prod-abc123/main_thumb.webp",
"originalUrl": "https://cdn.example.com/tenant-001/products/prod-abc123/main_original.jpg",
"width": 1200,
"height": 1200,
"size": 245678,
"format": "webp"
}

2. 刪除照片

  • 端點: DELETE /api/v1/products/{productId}/images/{imageId}
  • 權限要求: ROLE_MANAGER 或更高

響應: 204 No Content

3. 更新照片順序

  • 端點: PUT /api/v1/products/{productId}/images/order
  • 權限要求: ROLE_MANAGER 或更高

請求 Payload:

{
"galleryImageIds": ["img-001", "img-003", "img-002"] // 新順序
}

響應: 200 OK

4. 設定主圖

  • 端點: PUT /api/v1/products/{productId}/main-image
  • 權限要求: ROLE_MANAGER 或更高

請求 Payload:

{
"imageId": "img-003" // 將此副圖設為主圖
}

響應 Payload:

{
"mainImageUrl": "https://cdn.example.com/.../main.webp",
"galleryImageUrls": ["https://..."] // 更新後的副圖列表
}

前端驗證

// 檔案驗證
const validateImageFile = (file: File): ValidationResult => {
const MAX_SIZE = 10 * 1024 * 1024; // 10MB
const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'];
const MIN_DIMENSION = 200;
const MAX_DIMENSION = 4096;

if (file.size > MAX_SIZE) {
return { valid: false, error: '照片大小不能超過 10MB' };
}

if (!ALLOWED_TYPES.includes(file.type)) {
return { valid: false, error: '僅支援 JPEG、PNG、WebP 格式' };
}

// 需要讀取圖片後驗證尺寸
return { valid: true };
};

// 數量驗證
const validateGalleryCount = (currentCount: number, addCount: number): ValidationResult => {
const MAX_GALLERY = 5;
if (currentCount + addCount > MAX_GALLERY) {
return { valid: false, error: `副圖最多 ${MAX_GALLERY} 張,請先刪除現有照片` };
}
return { valid: true };
};

元件架構

src/applets/product-applet/
├── components/
│ ├── ProductImageUploader.tsx # 照片上傳主組件
│ ├── MainImageUploader.tsx # 主圖上傳區域
│ ├── GalleryImageUploader.tsx # 副圖管理區域
│ ├── ImageDropzone.tsx # 拖曳上傳區域
│ ├── ImagePreview.tsx # 單張照片預覽
│ ├── ImageUploadProgress.tsx # 上傳進度指示器
│ ├── ImageLightbox.tsx # Lightbox 預覽
│ └── SortableImageGrid.tsx # 可排序的照片網格
├── hooks/
│ ├── useImageUpload.ts # 照片上傳邏輯
│ ├── useImageDragDrop.ts # 拖曳上傳邏輯
│ ├── useImageSorting.ts # 照片排序邏輯
│ └── useLightbox.ts # Lightbox 控制邏輯
└── utils/
├── imageValidation.ts # 照片驗證工具
└── imageCompression.ts # 前端壓縮(可選)

依賴套件

{
"dependencies": {
"react-dropzone": "^14.x", // 拖曳上傳
"@dnd-kit/core": "^6.x", // 拖曳排序
"@dnd-kit/sortable": "^7.x", // 可排序列表
"yet-another-react-lightbox": "^3.x" // Lightbox
}
}

測試需求 (Test Requirements)

單元測試

  • 檔案驗證邏輯(大小、格式、尺寸)
  • 照片數量驗證邏輯
  • 排序更新邏輯
  • 主圖切換邏輯

整合測試

  • 照片上傳 API 整合
  • 照片刪除 API 整合
  • 排序更新 API 整合
  • 主圖設定 API 整合
  • CDN URL 生成正確性

E2E 測試

  • Scenario 1: 上傳商品主圖
  • Scenario 2: 拖曳上傳主圖
  • Scenario 3: 上傳多張副圖
  • Scenario 4: 拖曳排序副圖
  • Scenario 5: 設定副圖為主圖
  • Scenario 6: 刪除副圖
  • Scenario 8: 照片大小超過限制
  • Scenario 9: 照片格式不支援
  • Scenario 10: 副圖數量超過限制
  • Scenario 12: 預覽商品照片

驗收檢查清單 (Acceptance Checklist)

  • 主圖上傳功能正常
  • 拖曳上傳功能正常
  • 多檔上傳功能正常
  • 副圖排序功能正常
  • 設定副圖為主圖功能正常
  • 刪除副圖功能正常
  • 更換主圖功能正常
  • 檔案大小驗證正常
  • 檔案格式驗證正常
  • 副圖數量限制正常
  • 上傳進度顯示正常
  • 上傳失敗時顯示錯誤訊息
  • Lightbox 預覽功能正常
  • 響應式設計正常(桌面/平板/手機)
  • 多租戶隔離正常

Story Points

估算: 5 points

理由:

  • 照片上傳邏輯較複雜(支援拖曳、多檔、進度)
  • 拖曳排序需要整合 DnD 套件
  • Lightbox 預覽需要整合第三方套件
  • 需要處理各種錯誤情況
  • 後端需要整合雲端儲存

相關文檔


最後更新: 2025-12-03 撰寫者: AI Assistant