※ 訂單開立流程

- 前端資料驗證。
- 將商品的數量寫入(預扣)→ id。
※ 從前端需要傳入的資料:
商品ID、商品數量、使用哪個payment provider、 使用哪個payment way
※ 訂單Model(模型):model –––> order.ts

定義了一個類別 OrderModel
,所需要的訂單(Order
)資料格式:
- 用於處理與訂單相關的資料和操作。
import { Base } from "./base";
//PaymentProvider 介面:定義支付平台的結構。
export enum PaymentProvider {
ECPAY = "ECPAY",
PAYPAL = "PAYPAL"
}
//PaymentWay 介面:定義支付方式的結構。
export enum PaymentWay {
CVS = 'CVS',
PAYPAL = 'PAYPAL',
}
//OrderStatus 介面:定義訂單狀態的結構。
export enum OrderStatus {
WAITING = 'WAITING',
SUCCESS = 'SUCCESS',
FAILED = 'FAILED',
CANCEL = 'CANCEL',
}
//訂單的詳細資料
export interface OrderContent {
productId: number;
amount: number;
price: number;
}
//Order 介面定義了訂單的結構
export interface Order {
id: string;
total: number;
createdAt: Date;
updatedAt: Date;
paymentProvider: PaymentProvider;
paymentWay: PaymentWay;
status: OrderStatus;
contents: OrderContent[];
}
1.匯入模組:
import { Knex } from "knex";
import { Base, IBase } from "./base";
程式碼解說:
- 從 knex 模組匯入 Knex 類型,用於資料庫操作。
- 因為order需要繼承base,所以從本地模組 ./base 匯入 Base 類別和 IBase 介面。
2.定義 Order
介面:
export interface Order {
id: string;
total: number;
createdAt: Date;
updatedAt: Date;
paymentProvider: PaymentProvider;
paymentWay: PaymentWay;
status: OrderStatus;
contents: OrderContent[];
}
程式碼解說:
export interface Order {}
:定義了一個Order
介面,描述訂單的結構。- 定義訂單的結構,包括 ID、總金額、創建時間、更新時間、支付提供者、支付方式、訂單狀態和訂單內容。
3.用枚舉來定義支付平台、支付方式和訂單狀態的結構:
export enum PaymentProvider {
ECPAY = 'ECPAY',
PAYPAL = 'PAYPAL',
}
export enum PaymentWay {
CVS = 'CVS',
PAYPAL = 'PAYPAL',
}
export enum OrderStatus {
WAITING = 'WAITING',
SUCCESS = 'SUCCESS',
FAILED = 'FAILED',
CANCEL = 'CANCEL',
}
程式碼解說:
- 定義了三個枚舉:PaymentProvider、PaymentWay 和 OrderStatus,用於表示支付平台、支付方式和訂單狀態。
4.定義 OrderContent
介面:訂單的詳細資料
export interface OrderContent {
productId: number;
amount: number;
price: number;
}
程式碼解說:
- 定義訂單內容,包括產品 ID、數量和價格。
5.定義訂單模型,對應到資料庫中的 orders 表:
export class OrderModel extends Base<Order> implements IBase<Order> {
tableName = 'orders';
//屬性會自動對應到資料庫表中的相應欄位
schema = {
id: 'id',
total: 'total',
createdAt: 'created_at',
updatedAt: 'updated_at',
paymentProvider: 'payment_provider',
paymentWay: 'payment_way',
status: 'status',
contents: 'contents',
}
}
程式碼解說:
- 定義了一個類別
OrderModel
,它繼承了Base<Order>
。這表示OrderModel
類別將具有Base
類別的所有屬性和方法,但它特別針對Order
這個型別進行操作。 - tableName 屬性:指定資料表名稱為 orders。
- schema 屬性:定義資料表欄位與模型屬性的映射關係。
6.靜態方法 createModel
:專門用來生成模型(model)
static createModel =
({ knexSql, tableName }: { knexSql: Knex; tableName?: string })
=> {
return new OrderModel({ knexSql, tableName });
}
程式碼解說:
- 定義一個靜態方法 createModel,用於創建 OrderModel 實例。
7.建構函數:
constructor({ knexSql, tableName }: { knexSql: Knex, tableName?: string }) {
super({ knexSql, tableName });
}
程式碼解說:
- 初始化
OrderModel
實例:在創建OrderModel
實例時,可以傳入資料庫連接(knexSql
)和可選的資料表名稱(tableName
),這些參數將被傳遞給基類的建構函數,以正確初始化基類的狀態。 - 確保類別的繼承和初始化:通過調用
super
,確保基類的建構函數被正確調用,使得OrderModel
繼承了基類的屬性和方法。
8.定義一個有基礎(Base)介面中所有功能的訂單(Order):
export interface IOrderModel extends IBase<Order> {
create(orderData: Omit<Order, "id"> & { id?: string },
trx?: Knex.Transaction): Promise<Order | null>; }
程式碼解說:
- 使用 export 導出這個介面,使其可以在其他模組中使用。
- 使用 interface 關鍵字定義介面 IOrderModel。
- extends IBase<Order> 表示 IOrderModel 繼承自 IBase<Order> 介面。
- 這意味著 IOrderModel 會擁有 IBase<Order> 定義的所有屬性和方法。
- 參數:
- orderData:這是一個物件,類型是 Omit<Order, "id"> & { id?: string },表示 orderData 具有 Order 的所有屬性(除了 id),並且有一個可選的 id 屬性。
- trx:這是一個可選的參數,類型是 Knex.Transaction,用於資料庫交易處理。
- 返回值:這個方法返回一個 Promise,表示這個方法是非同步的。Promise 的值可以是 Order 或 null
9.IBase<Order>改成 IOrderModel:
export class OrderModel extends Base<Order> implements IOrderModel
※ 建立ORDER的CONTROLLER,處理訂單相關的 API 請求:controller –––> orderController.ts

1.匯入模組:
import { IOrderModel, OrderContent, PaymentProvider, PaymentWay } from "@/model/order";
import { IProductModel } from "@/model/product";
import { NextFunction, Request, Response } from "express";
import { Knex } from "knex";
程式碼解說:
- 從 @/model/order 和 @/model/product 匯入相關的介面和類型。
- 從 express 匯入 NextFunction, Request 和 Response。
- 從 knex 匯入 Knex,用於資料庫操作。
2.定義 IOrder功能:
export interface IOrderController {
createOrder(
req: Request<any, any, CreateOrderRequestParams, any>,
res: Response,
next: NextFunction
): void;
updateAmount(
req: Request<any, any, any, any>,
res: Response,
next: NextFunction
): void;
}
程式碼解說:
- createOrder 方法:
- 用途:用於創建新訂單。
- 參數:req:型別為 Request<any, any, CreateOrderRequestParams, any>,包含 CreateOrderRequestParams 介面定義的參數(支付提供者、支付方式和訂單內容)。
- res:型別為 Response,用來回應客戶端請求。
- next:型別為 NextFunction,用來調用下一個中介軟體。
- updateAmount 方法:
- 用途:用於更新訂單中的商品數量。
- 參數:req:型別為 Request<any, any, any, any>,沒有具體定義請求體的結構。
- res:型別為 Response,用來回應客戶端請求。
- next:型別為 NextFunction,用來調用下一個中介軟體。
3.定義 OrderController
類別:
export class OrderController implements IOrderController {
knexSql: Knex;
orderModel: IOrderModel;
productModel: IProductModel;
constructor({ knexSql, orderModel, productModel }: {
knexSql: Knex;
orderModel: IOrderModel;
productModel: IProductModel;
}) {
this.knexSql = knexSql;
this.orderModel = orderModel;
this.productModel = productModel
}
}
程式碼解說:
- 繼承 IOrderController:這意味著 OrderController 必須實現 IOrderController 介面定義的方法。
- 成員變數宣告:在類別
OrderController
中,宣告了三個成員變數knexSql
、orderModel
和productModel
。這些變數的類型分別是Knex
、IOrderModel
和IProductModel
,用來存儲傳遞給建構子函數的值。 - 建構函數接受 knexSql, orderModel 和 productModel 作為參數,並將它們賦值給類別的屬性。
- 將傳入的
knexSql
資料庫連接實例賦值給this.knexSql
屬性。 - 將傳入的
orderModel
實例賦值給this.orderModel
屬性,這樣可以在OrderController
中方便地操作訂單資料。 - 將傳入的 productModel 實例賦值給 this.productModel 屬性,確保可以在 OrderController 中操作與訂單相關的產品資料。
4.定義了一個 createOrder
的公開方法,:
public createOrder: IOrderController["createOrder"] = (req, res, _next) => {
let { paymentProvider, paymentWay, contents } = req.body
}
程式碼解說:
public createOrder
:這表示這個方法是公開的,能夠從類別外部存取。IOrderController["createOrder"]
:這表示這個方法的型別必須符合IOrderController
介面中的createOrder
方法的定義。(req, res, _next) => {}
:這是一個箭頭函數,接受三個參數req
(請求物件)、res
(回應物件)、_next
(下一個中介軟體函數,這裡沒用到,所以用_
前綴表示未使用)。- 使用解構賦值,從請求物件
req.body
中提取paymentProvider
、paymentWay
和contents
這三個屬性。 paymentProvider
:支付提供者,例如 PayPal、信用卡等。paymentWay
:支付方式,例如在線支付、貨到付款等。contents
:訂單的內容,通常是包含商品詳細資訊的數據。
5.定義 CreateOrderRequestParams
介面:請求物件 req.body 的結構
interface CreateOrderRequestParams {
paymentProvider: PaymentProvider;
paymentWay: PaymentWay;
contents: OrderContent[];
}
程式碼解說:
- 定義了 CreateOrderRequestParams 介面,用來描述創建訂單請求的參數結構。
- paymentProvider:枚舉類型 PaymentProvider,指明支付提供者,如 ECPAY 或 PAYPAL。
- paymentWay:枚舉類型 PaymentWay,指明支付方式,如 CVS 或 PAYPAL。
- contents:一個 OrderContent 陣列,描述訂單中的每個產品項目。
6.修改IOrder功能:
createOrder(req: Request<any, any, CreateOrderRequestParams, any>, res: Response, next: NextFunction): void;