狀態值使用具語意的字串,不使用數字代碼。 --- 在設計訂單資料庫欄位的時候,狀態這個欄位應該使用哪一種資料型態呢?答案是統一使用 varchar,也就是字串來表達狀態。 我看過很多團隊會使用 integer 或 tiny integer 這種方式來表達。很常見的就是用 0、1、2...
訂單、付款、履約與配送狀態,應該要分開表示。 --- 台灣的中小企業做電商系統時,訂單的狀態相關欄位應該要有四個欄位,而不是只有一個狀態欄位。 我知道很多團隊開發的時候,orders 那張 table 就只有一個 status 欄位,然後用它來表達未付款、已付款、待出貨、已出貨、已...
訂單金額使用固定四欄位:items_subtotal、discount_total、shipping_fee、total --- 我看過很多團隊,他們價格相關的欄位設計亂七八糟。為了得到訂單的總價,有時候幾個欄位要相加,有時候要相減。上線之後,code 就很難讀,bug 也一大堆。 ...
猶豫不決的時候,先去查 Shopify 的做法與單字用語。 --- 在設計資料結構、資料格式、系統架構,以及英文命名語感時,可以多參考 Shopify。 首先,Shopify 是全球市值前百大的公司,也是在電商領域累積大量經驗的平臺。各種電商問題,他們幾乎都已經思考過;對臺灣開發者...
訂單是 `orders`,訂單內容則是 `order_items`。 --- `商品` 則需要先和客戶或老闆確認:公司商品是否有分規格? 所謂規格,例如服飾有 S、M、X、L 等尺寸,或紅、橙、黃、綠、藍等顏色;若有 N 種顏色與 M 種尺寸,就會產生 N × M 種規格。化妝品的...
商品的核心資訊和銷售資訊,建議分開存放。 --- 核心資訊可以放在 `products` 資料表,例如商品名稱,以及和商業邏輯直接有關的欄位,像是是否支援訂閱、是否能直接購買等。這些資料會影響購物車、結帳和付款等重要功能。 很多團隊的 `products` 資料表最後會變得很大,但...
設計一套簡單通用的圖片 API,架構會更簡潔,也更容易維護。 --- 電商後臺本質上接近 CMS,常見需求包含商品多圖、首頁橫幅、部落格封面與公告圖片等。 建立統一的後端圖片上傳 API,並加入基本驗證,例如既有的 Session 登入驗證,避免外部任意呼叫或濫用圖片 API。 ...
統一產生 3 至 6 種固定尺寸,就能涵蓋各種使用情境了。 --- 企業電商 CMS 的圖片規格,各區塊所需尺寸不同,但不必把流程設計得太複雜。 後端圖片上傳後,除保留原始檔外,統一產生 3 至 6 種固定尺寸。若沒有明確規格,可先採用六種: xs:160px sm:3...
使用 ULID 命名資料夾路徑,管理圖片與尺寸會很漂亮。 --- 無論是圖片儲存位置或資料庫,都不需要保留原始檔名,也不必使用冗長的 UUID 或隨機字串。 ULID 長度適中,且可依時間排序,結合尺寸與 namespace 的概念後,整體結構會更清楚。 副檔名如 jpg、p...
不需要把各種付款方式的交易紀錄,抽象成同一張資料表。 --- 每種外部金流流程都使用獨立的 Transaction Table,不必過度抽象。命名格式為: `[RESOURCE]_[VENDOR]_[METHOD]_transactions` Table 名稱直接表達:服務哪...
所有金流交易資料表,都用相同 schema 即可。 --- 如果支援五、六種付款方式,就會有五、六張金流 Transaction Table。你可能會覺得每張都要設計很麻煩,但其實所有金流 Table 都可以用同一種格式。 每張 Table 只需要五個欄位: 1. id ...
串接金流時,最困難的通常是付款流程設計。分享一個簡單的設計模式。 --- 無論提供五種、十種或更多付款方式,每一種金流最終只需要三個函數: 1. Start:開始付款流程。 2. Return:付款完成後跳回系統的網址。 3. Notify:金流商通知付款結果的 Cal...
簡化成一種控制流程,讓系統簡單好維護。 --- 開發金流時常見一個問題:使用者付款後跳回付款結果頁或訂單詳情頁,但廠商的 notify callback 可能慢了幾毫秒才到。 此時若訂單狀態尚未更新,使用者可能會看到「付款未完成」。 這種體驗很差,必須確保使用者付款成功後看到...
從金流跳回 return 頁面時,改善等待體驗的簡單方法。 --- 可建立一個通用的「處理中,請稍候...」頁面,提供一個 routing 打開這頁,畫面設計得簡潔美觀。此 routing 接收 `to` URL 參數,用來指定處理完成後要跳轉的目的頁面。 系統中任何需要等待的流程...
建立一張訂單內部備註資料表,讓後台營運更方便。 --- 新增一張 `order_notes` table - id - order_id - user_id - content - created_at 管理人員開啟訂單詳情時,可在頁面下方新增留言,作為內部筆記使用。...
保留內部操作歷程,方便日後追蹤與瞭解訂單狀況。 --- 適合中小企業電商後台的小架構:新增一張 `order_actions` table - id - order_id - user_id - description - created_at 當系統發生特定操作,例...
一個能讓電商後台訂單管理更清楚、好用的功能:訂單歷程 UI。 --- 這是我研究 Shopify 時學到的概念。Shopify 的訂單 Timeline 區塊非常實用,值得借鑑它的精神。 在訂單細節頁下方新增一個「訂單歷程」時間軸,整合所有與該訂單相關的紀錄,包括金流交易、物流交易...
用簡單的 Schema,處理系統中複雜且持續擴充的行銷模組。 --- 公司可能有折扣碼、滿額折抵、檔期活動等,各自都會影響訂單金額。這時不要把訂單欄位設計得太複雜,可以建立一張 `order_discounts` 表,欄位包含: `id`、`order_id`、`label`、`a...
購物車存的是「客戶做了什麼選擇」,不是「算出來多少錢」。 --- 我看過很多購物車,session 裡面長這樣: ```text items、subtotal、discount、shipping_fee、total、free_shipping、gift_name ... ```...
一個是客戶填的,一個是系統算的。混在一起就會爛。 --- 購物車最常見的設計錯誤,是只有一個「購物車物件」。 那個物件裡面同時有 `items`、`coupon`,也有 `subtotal`、`discount`、`total`、`gifts`。它從 session 讀出來、被 c...
規則寫在建構子,就不可能有人繞過它。 --- 購物車有一些「永遠不該發生」的狀態。例如: - 購物車裡只有加價購商品,沒有任何正常商品。 - 購物車是空的,但還留著折扣碼跟點數折抵。 - 某個商品的數量是 0,或是 9999。 這些規則通常會被寫在 controller:...
所有促銷排成一條 pipeline,順序寫死在同一個檔案裡。 --- 電商做久了,促銷一定會變多。滿額折扣、滿額贈品、折扣碼、加價購、點數折抵、點數回饋⋯⋯每一種都會動到金額。 多數人的寫法是在 controller 或某個 `CartService` 裡面,一段一段 `if` 疊...
促銷規則的型態一律是:`(設定, CartState) → CartState`。 --- 上一篇講 pipeline 的形狀,這篇講裡面那些規則怎麼寫。 我的規定很簡單,**每條促銷規則都是一個普通的靜態純函式,型態長得一模一樣**: ```php public stat...
行銷規則的爭議幾乎都是「用哪個小計當基準」,備好四個就夠了。 --- 行銷單位提的需求,聽起來每次都很新。但你把它翻成程式碼,會發現爭議永遠只有同一個: **這一折,是折在哪個數字上?** - 滿 3000 折 10%,那個 3000 含不含加價購? - 折扣碼跟滿額折扣同...
從 DB 撈出來的促銷設定,先組成一份 Catalog,再丟進計價邏輯。 --- 這是一個很小的設計,但它決定了你的促銷模組能不能長期維護。 促銷規則需要參數:門檻是多少、折幾折、送什麼贈品、加購價多少。這些參數住在資料庫,而且會被後台改來改去。 很多人的寫法是,在促銷規則裡...
凡是會出貨的東西,就是一筆 order item,用 `type` 區分來源。 --- 購物車裡除了客戶自己選的商品,還會冒出兩種東西:**滿額贈品**、**加價購商品**。 我看過幾種處理方式:贈品另外開一張 `order_gifts` 表;加價購塞進 `order_items`...
先收斂出一份「一張訂單所需的全部事實」,再落地成 Order。 --- 結帳 controller 是電商裡最容易爛掉的地方。它要處理表單驗證、超商門市、發票欄位、運費、付款方式、訂單建立、寄信、導向金流⋯⋯一路寫下來,八百行起跳。 我讓它不爛的方法是:**在寫進資料庫之前,先把所...
達標提示是對購物車的唯讀投影,不是另外寫一份判斷。 --- 購物車頁很常需要這種提示: ```text ✓ 滿 3000 折扣 10% 滿 5000 送旅行組,還差 $1,400 ``` 這個功能看起來很單純,所以通常是前端工程師或某個趕時間的人,隨手在 blade...
購物車與定價用 DTO,其餘模組直接用 Eloquent。 --- 我的系統裡有兩種寫法。 橫幅、公告、商品、分類這些模組,後台 controller 就是這樣: ```php public function store(Request $request) { ...
結帳全部算完,最後才一次寫進資料庫。 --- 很多人的結帳流程是這樣寫的: ```text 建立訂單 → 算商品小計 → 更新訂單 → 套用折扣 → 寫一筆折扣 → 更新訂單 → 算運費 → 更新訂單 → 扣點數 → 更新訂單 → 發回饋點數 → 更新訂單 ``` 一路...
兩邊要的東西不一樣,不必硬用同一套 CSS。 --- 我的專案裡有兩套 CSS 框架。前台用 Tailwind 4,後台用 Bootstrap 5。 每次講到這件事,都有人皺眉頭:不一致、學習成本、包太大。 我認為這是對的選擇,理由很簡單:**這兩個介面的目標完全相反。** ...
頁面用 Blade 渲染,只有需要互動的地方才放一個元件進去。 --- 一個電商網站,真正需要互動的地方有幾個? 購物車、抽屜購物車、搜尋、手機選單、加入購物車按鈕、後台的複雜表單。大概就這些。 其他九成的畫面——首頁、商品列表、商品內容、文章頁、關於我們、訂單詳情——都是伺...
用 htm 取代 JSX,瀏覽器直接跑,不需要編譯。 --- 我的 `package.json` 裡面**沒有 Preact**。 ```json "devDependencies": { "@tailwindcss/vite": "^4.0.0", "lar...
購物車頁的元件,就住在購物車頁的檔案裡。 --- 前端專案的標準做法,是把元件收在一個資料夾: ```text resources/js/components/CartPage.jsx resources/js/components/SearchModal.jsx resou...
不需要狀態管理,`CustomEvent` 就夠了。 --- 用 islands 架構,一定會遇到這題:**兩個互不相干的元件,要怎麼同步?** 我的網站上,購物車狀態同時被三個地方用到: - 導覽列右上角的數量角標 - 抽屜購物車 - 購物車頁 它們是三個獨立掛載...
客戶改了數量,後端重算一遍,把完整的購物車丟回來。 --- 購物車的前端有一個很大的誘惑:**數量 +1,前端自己把小計乘一乘就好了嘛。** 不要。 因為那個小計不是乘一乘就有的。它牽涉到滿額折扣有沒有跨門檻、滿額禮要不要出現、折扣碼的折扣基準要不要跟著變、點數折抵上限要不要...
核心邏輯用領域命名,不要放進 `Services/` 那種技術分類。 --- 我的 `app/` 長這樣: ```text app/ Commerce/ ← 自己加的 Support/ ← 自己加的 Console/ Http/ M...
一張 `settings` 表搞定所有後台設定,不要每加一個設定就改 schema。 --- 電商系統的設定會一直長:運費多少、免運門檻、哪幾家超商開啟、點數怎麼回饋、折抵上限幾 %、發票要不要自動開⋯⋯ 每加一個就開一個欄位,你的 migration 會變成一部編年史。每加一個就...
客戶按下結帳就建單,不管他後來有沒有付錢。 --- 「訂單什麼時候建立?」這題的答案會決定你的系統好不好維護。 有些人選「付款成功才建立」,理由聽起來很合理:沒付錢的不算訂單,資料庫才不會一堆垃圾。 實際上這條路走不通。 **因為有些付款方式,本來就需要訂單先存在。**...
用 Email 寄 6 或 8 碼驗證碼就好,整個系統沒有密碼欄位。 --- 我的電商網站沒有密碼。 登入流程是:輸入 Email → 收信拿到 6 碼驗證碼 → 輸入 → 登入完成。 沒有註冊表單、沒有設定密碼、沒有忘記密碼、沒有重設密碼信、沒有密碼強度規則。 ## ...
外部系統回傳的資料寫進 server 端 cache,網址只帶一把鑰匙。 --- 超商取貨選店的流程,是這樣的: ```text 結帳頁 → 導去綠界電子地圖 → 客戶選好門市 → 綠界 POST 回你的網站 ``` 問題來了:綠界把門市資料 POST 回來之後,你要怎麼...
會員可用點數是「算出來的」,不是存在某個欄位裡的。 --- 九成的點數功能,是這樣做的: ```php $user->increment('points', 150); $user->decrement('points', 600); ``` `users` 表加一個 ...
下單時只存快照,付款成功才真的動帳本。 --- 點數的時間點很容易搞錯。 **下單就扣點、下單就發點,是錯的。** 客戶選了 ATM,三天沒去繳款,那張單作廢了——但他的點數已經被扣掉、回饋也已經發出去了。 正確的順序是這樣: ```text 結帳 算出這張單「...
不要把訂閱做成一種特殊的訂單,讓它去「生」訂單。 --- 做定期購的時候,第一個要決定的是:**訂閱資料要放哪裡?** 最直覺的做法是在 `orders` 加幾個欄位——`is_subscription`、`cycle_days`、`next_billing_date`——然後讓這...
續期建立訂單時,只讀訂閱自己,不回頭讀 products。 --- 訂閱最容易被忽略的一件事:**它會活很久。** 客戶今年三月訂的,可能扣到明年。這段期間,商品會發生什麼事? - 改名字(「保濕精華」變成「保濕精華 2.0」) - 漲價 - 下架 - 關閉訂閱功能 ...
後台權限由環境變數決定:正式環境一律擋,本機一律放行。 --- 後台開發最煩的事,是每次 `migrate:fresh` 之後,都要先去建一個帳號、登入、給權限,才能看到你正在做的那個頁面。 我的做法是讓權限跟著環境走: ```php // 後台一律要求登入與 admin ...
資料庫砍掉重建是日常操作,不是災難。 --- 我對專案的一個硬性要求:**任何時候、任何人,執行這一行都要能得到一個完整可用的系統。** ```bash php artisan migrate:fresh --seed ``` 砍光資料表、重建、塞資料。三十秒後,你有商品...
讓付款那一步可以被跳過,後面的流程才測得動。 --- 電商最難測的不是付款,是**付款之後的那一長串**: ```text 付款成功 → 訂單轉 paid → 結算點數 → 寄通知信 → 開立發票 → 推送物流單 → 訂閱推進期數 ``` 這串東西你想測一次,就得先刷一次...
每個模組先做一個最小可執行版本,然後不要刪掉它。 --- 我的專案有一批只在 local / dev / testing 註冊的路由: ```text /_poc/images 圖片上傳機制 /_poc/email-otp Email OTP 登入流程 ...
自增 id 對內、ULID 對外、可讀單號給顧客。三個各司其職。 --- 一張訂單,我給它三個識別碼: ```text id 1024 自增,資料庫內部用 public_id 01J8K2M...(ULID) ...
三種憑據擇一:登入的會員、這個 session 剛下的單、信裡的簽章連結。 --- 台灣的電商,訪客結帳佔比很高。客戶不想註冊,填完資料就付款走人。 問題來了:**他要怎麼回來看訂單?** 他沒有帳號可以登入,所以你不能用 `user_id` 比對。但訂單頁又不能隨便給人看—...