第三方金流串接 – 產品Model(模型)

更新於 發佈於 閱讀時間約 40 分鐘

※ 認識MVC架構:

  • Model (模型):
    • Data Model: 負責資料庫的交互與資料的映射,通常用來定義資料結構和進行資料庫操作。
    • Business Model: 處理商業邏輯,將原始資料進行處理和轉換(在這次的設計中,這部分由 Controller 處理,省略business model的開發。)。
  • Controller (控制器):
    • 從 Model 取得資料並進行處理,根據商業邏輯整理好後傳給 View。
    • 就是負責調控和協調。
  • View (視圖):
    • 負責將資料呈現給使用者,通常是前端的畫面渲染。View 不包含任何邏輯處理,專門做畫面的渲染。

※ MVC架構的優點:

可以將資料做分層化的處理,也就是單一職責原則。

raw-image




※ 建立Model :model --> base.ts

建立Model 的主要功能有:

  • 執行 CRUD(建立、讀取、更新、刪除)操作。
raw-image

※ 定義接口(IBase) :

export interface IBase<T> {
findAll(): Promise<T[] | null>;
findOne(id: any): Promise<T | null>;
create(date: Omit<T, 'id'>): Promise<T | null>;
update(id: any, date: Partial<Omit<T, 'id'>>): Promise<T | null>;
delete(id: any): Promise<void>;

}

程式碼解說:

  • export interface 就是把一個物件結構的描述從一個模組拿出來,讓其他模組也能使用這個結構定義。
  • IBase 前綴 "I"明確地表示這是一個接口,所以IBase 表示這是一個基礎接口。
  • 泛型 T: 使用泛型 T 使這個接口可以適用於各種類型的資料,不限於特定資料結構。
  • Promise: 使用 Promise 以便在非同步操作(如資料庫查詢)完成後處理結果。
  • Omit 和 Partial: 使用 OmitPartial 來靈活地定義資料結構,避免在創建和更新時強制要求提供所有欄位。
  • findAll(): Promise<T[] | null>:它會取得資料庫中所有的記錄。
  • findOne(id: any): Promise<T | null>:根據 ID 查找特定的資料。
  • create(data: Omit<T, 'id'>): Promise<T | null>:接受一個不包含 id 的資料物件(Omit<T, 'id'>),用於在資料庫中建立新記錄。
  • update(id: any, data: Partial<Omit<T, 'id'>>): Promise<T | null>:

接受一個 ID 和一個部分更新的資料物件(Partial<Omit<T, 'id'>>),用於根據 ID 更新現有記錄。

delete(id: any): Promise<void>:

根據所給的 ID 刪除資料記錄,void表示操作完成且無返回值,於刪除資料庫中的記錄。

※ 建立database transaction,資料庫交易 :

export interface IBase<T> {
findAll(trx?: Knex.Transaction): Promise<T[] | null>;
findOne(id: any, trx?: Knex.Transaction): Promise<T | null>;
create(data: Omit<T, 'id'>, trx?: Knex.Transaction): Promise<T | null>;
update(id: any, data: Partial<Omit<T, 'id'>>, trx?: Knex.Transaction): Promise<T | null>;
delete(id: any, trx?: Knex.Transaction): Promise<void>;
}

程式碼解說:

  • trx?表示這個參數是可選的,不傳遞也沒關係。它是 Knex.js 用於資料庫交易(transaction)的物件。
  • Omit:目的是排除某個屬性。在創建資料時,由系統自動生成或管理的屬性,有ID、時間戳(created_at 和 updated_at)、唯一標識碼(UUID)、系統內部狀態。
  • findAll(trx?: Knex.Transaction): Promise<T[] | null>; 查找所有記錄,返回一個包含所有記錄的 Promise,類型為陣列(T[]),或是空值(null)。
  • findOne(id: any, trx?: Knex.Transaction): Promise<T | null>; 根據 ID 查找單一記錄,返回一個包含該記錄的 Promise,類型為 T 或是空值(null)。
  • create(data: Omit<T, 'id'>, trx?: Knex.Transaction): Promise<T | null>; 創建一個新的記錄,傳入的資料不包含 ID(Omit<T, 'id'>),返回一個包含創建結果的 Promise,類型為 T 或是空值(null)。
  • update(id: any, data: Partial<Omit<T, 'id'>>, trx?: Knex.Transaction): Promise<T | null>: 說的是,更新指定 ID 的記錄,傳入需要修改的屬性,返回一個包含更新結果的 Promise,類型為 T 或是空值(null)。
  • delete(id: any, trx?: Knex.Transaction): Promise<void>; 根據 ID 刪除記錄,返回一個 Promise,完成後不返回數據。
  • T 表示單一的一個資料元素,而 T[] 則表示一個包含多個資料元素的陣列。也就是說,查詢所有資料用 T[] 表示資料陣列,查詢單一資料或創建新資料用 T 表示單個資料。
  • void 是 TypeScript 和 JavaScript 中用來表示「沒有返回值」的函數。

※ 定義抽象類別(Base):

export abstract class Base<T> implements IBase<T> {
constructor({ knexSql, tableName }: { knexSql: Knex, tableName?: string }) {

}
}

程式碼解說:

  1. export abstract class Base<T> implements IBase<T>:
    • 定義了一個泛型抽象類別 Base這個框架,來實現 IBase<T> 這個介面。這表示 Base 類別必須包含 IBase 介面定義的方法,但因為它是抽象類別,所以不能直接實例化。
  2. constructor 方法
  • constructor({ knexSql, tableName }: { knexSql: Knex, tableName?: string }) 允許在創建 Base 類實例時傳入一個包含 knexSql 和可選的 tableName 的物件。
  1. 從 App 傳入參數
  • 在 App 中,可以根據需要傳入這些參數,當創建 Base 或其子類的實例時,提供所需的資料庫連接和表名。
  • 如果 tableName 未傳入,則可以在子類或其他地方決定其具體值。


export abstract class Base<T> implements IBase<T> {
//定義屬性:
protected knexSql: Knex;
protected tableName: string = '';
protected schema = {};
//在建構函數中初始化和設定這些屬性,以確保類的實例具有正確的初始狀態。
constructor({ knexSql, tableName }: { knexSql: Knex, tableName?: string }) {
//設定值:
this.knexSql = knexSql;
if (tableName) this.tableName = tableName;
}

程式碼解說:

  1. protected knexSql: Knex;
    • 定義了一個受保護的屬性 knexSql,類型為 Knex。這表示這個屬性只能在 Base 類別及其子類中使用。目的是增強程式碼的封裝性和安全性用來存儲資料庫連接實例。
  2. protected tableName: string = '';
    • 定義了一個受保護的屬性 tableName,類型為 string,並預設值為空字串。
  3. protected schema = {};
    • 定義了一個受保護的屬性 schema,預設為空物件。這可能用來存儲資料庫表格的結構定義。
  4. this.knexSql = knexSql;:將傳入的 knexSql 參數賦值給 this.knexSql,將資料庫連接實例保存到該類的屬性中。
  5. if (tableName) this.tableName = tableName;:是否提供了 tableName 參數,如果提供,則將其賦值給 this.tableName。

※ 將資料庫中的資料轉換為應用程式中的資料格式:

認識lodash:

Lodash 是一個很實用的function集合包,包含了所有的函數和功能。目的是為了簡化重複性的代碼,減少開發時間和錯誤。

Lodash 中常用的功能:

  1. 陣列處理:_.map, _.filter, _.reduce 等函數,讓你可以方便地操作陣列。
  2. 物件處理:_.assign, _.cloneDeep, _.merge 等函數,讓物件的操作變得簡單。
  3. 數據查詢:_.find, _.findIndex, _.includes 等函數,幫助你快速查找數據。
  4. 函數處理:_.debounce, _.throttle, _.once 等函數,控制函數的調用次數。
  5. 字串處理:_.camelCase, _.kebabCase, _.startCase 等函數,方便格式化字串。

安裝lodash:

npm install lodash


認識@types/lodash:

這個是 TypeScript 的型別定義包,用於 Lodash 函式庫。當你在用 TypeScript 開發時,它提供 Lodash 函數的型別定義,這樣編譯器在使用 Lodash 時得到自動完成功能和型別檢查。

安裝@types/lodash:

npm install --save-dev @types/lodash


private DBData2DataObject = (data) => {
const transform = mapValues(data, (value, key) => {
if (['updatedAt', 'createdAt'].includes(key)) return new Date(value);

// check if a string is json
      if (typeof value === "object") return JSON.stringify(value);
      return value;
})
}

程式碼解說:

  1. private DBData2DataObject = (data) => { ... }:這是一個箭頭函數,它接收一個 data 參數。這個函數被定義為 private,表示它只能在類別內部使用。
  2. const transform = mapValues(data, (value, key) => { ... }):這一行使用 mapValues 函數來遍歷 data 中的每個鍵值對。mapValues 是一個類似於 Array.prototype.map 的函數,但它是用來處理物件的。對於 data 中的每個鍵值對,都會執行提供的箭頭函數 (value, key) => { ... }。
  3. if (['updatedAt', 'createdAt'].includes(key)) return new Date(value);:這一行檢查當前鍵(key)是否為 updatedAt 或 createdAt,如果是,則將對應的值轉換為 Date 對象並返回。
  4. if (typeof value === "object") return JSON.stringify(value);:這一行檢查當前值(value)是否為物件,如果是,則將其轉換為 JSON 字符串並返回。
  5. return value;:如果值不是物件,則直接返回該值。
  6. 最後,transform 變數將包含轉換後的資料。

這段程式碼的目的是將輸入資料中的 updatedAtcreatedAt 字段轉換為 Date 對象,並將所有物件型別的值轉換為 JSON 字符串。

※ 判斷輸入的字串是否為有效的 JSON 格式:utils --> index.ts

export const isJson = (value: string) => {

try {

return Boolean(JSON.parse(value));

} catch (e) {

return false;

}

}

程式碼解說:

  1. 函數定義
export const isJson = (value: string) => {}
  • export:表示這個函數可以被其他模組引用。
  • const isJson:定義了一個常數函數,名稱為 isJson。
  • (value: string):函數接受一個字串類型的參數 value。
  1. try 區塊:
try {

return Boolean(JSON.parse(value));

}
  • JSON.parse(value):嘗試將字串解析為 JSON。
  • Boolean():將解析結果轉換為布林值。如果解析成功,返回 true。
  1. catch 區塊
catch (e) {

return false;

}
  • 如果解析失敗,捕捉錯誤並返回 false。

※ 將資料庫中的資料轉換為應用程式中的資料格式:

private DBData2DataObject = (data: any) => {

const transform = mapValues(data, (value, key) => {

if (['updatedAt', 'createdAt'].includes(key)) return new Date(value);

if (isJson(value)) return JSON.parse(value);

return value;

});

return mapKeys(transform, (_value, key) => camelCase(key));

}

程式碼解說:

  • 私有方法
    • private DataObject2DBData = (data: any) => { ... }:這是一個私有的箭頭函數,僅供類的內部使用。
  • 轉換數據(mapValues):
    • 這行代碼會遍歷 data 中的每個屬性,並根據屬性名和屬性值進行處理。
    • 如果屬性名是 updatedAt 或 createdAt,就把這個屬性的值轉換成日期(Date)物件。
    • 如果屬性值是 JSON 字符串,就把這個字符串解析成 JavaScript 物件。
    • 對於其他情況,保持屬性值不變。
  • 鍵名轉換(mapKeys):
    • 這行代碼會遍歷轉換後的每個屬性,將屬性名從蛇形命名法(如 created_at)轉換成駝峰命名法(如 createdAt),使其符合 JavaScript 的命名慣例。

※ 導入isJson函數:

raw-image

※ data參數增加any:

private DBData2DataObject = (data: any) => {}
raw-image

※ 將應用程式中的資料物件轉換成適合存入資料庫的格式:

 private DataObject2DBData = (data: any) => {

    const transform = mapValues(data, (value, key) => {

      if (['updatedAt', 'createdAt'].includes(key)) return new Date(value);



      if (isJson(value)) return JSON.parse(value);

      return value;

    });

    return mapKeys(transform, (_value, key) => snakeCase(key));

  }

程式碼解說:

  • 將日期字段轉換為 Date 物件。
  • 將 JSON 字符串解析為 JavaScript 物件。
  • 將鍵名轉換為蛇形命名法。


※ CRUD的function:

1. 查詢所有記錄:

public findAll = async (trx?: Knex.Transaction) => {
//select col1, col2, ...from tableName
let sqlBuilder = this.knexSql(this.tableName).select(this.schema);

if (trx) sqlBuilder = sqlBuilder.transacting(trx);

const result = await sqlBuilder;

if (isEmpty(result)) return null;

return result.map(this.DBData2DataObject) as T[];
}

程式碼解說:

  1. 方法定義
public findAll = async (trx?: Knex.Transaction) => {}
  • public:表示這個方法是公開的,可以被外部訪問。
  • findAll:方法名稱。
  • async:表示這個方法是非同步的,會返回一個 Promise。
  • trx?: Knex.Transaction:可選的參數 trx,類型為 Knex 的交易物件。
  1. 建立 SQL 查詢
let sqlBuilder = this.knexSql(this.tableName).select(this.schema);
  • 使用 Knex.js 建立一個 SQL 查詢建構物件(sqlBuilder),從 this.tableName 表中選擇 this.schema 中定義的欄位。
  1. 交易處理
if (trx) sqlBuilder = sqlBuilder.transacting(trx);

如果有提供 trx 交易物件,則將查詢納入該交易,以確保資料的一致性和原子性。

  1. 執行查詢
const result = await sqlBuilder;
  • 執行查詢並等待結果。
  1. 結果處理
if (isEmpty(result)) return null;
return result.map(this.DBData2DataObject) as T[];
  • 將資料庫裡面的資料拉出來,如果結果為空,返回 null。
  • 如果有結果,會將結果轉換成資料物件,並以 T[] 型別返回。
  • 使用as T[]的原因:明確地指定返回的結果是一個具有型別 T 的數組。
raw-image


※ 查詢單筆記錄:

public findOne = async (id: any, trx?: Knex.Transaction) => {
let sqlBuilder = this.knexSql(this.tableName).select(this.schema).where(id);

if (trx) sqlBuilder = sqlBuilder.transacting(trx);

const result = await sqlBuilder;

if (isEmpty(result)) return null;

return this.DBData2DataObject(result[0]) as T;
}

程式碼解說:

1. 定義方法:

public findOne = async (id: any, trx?: Knex.Transaction) => { ... }
  • 這是一個公開的異步箭頭函數 findOne,接收兩個參數:id 和可選的交易 trx。
  • public 表示這個方法可以從類的外部訪問。
  1. 建構 SQL 查詢:
let sqlBuilder = this.knexSql(this.tableName).select(this.schema).where(id);
if (trx) sqlBuilder = sqlBuilder.transacting(trx);
  • 使用 knexSql 方法來構建 SQL 查詢,選擇 schema 中的字段,並根據 id 進行查詢。
  • 如果提供了交易 trx,則使用 transacting 方法將查詢包含在交易中。

3. 執行查詢:

const result = await sqlBuilder;

執行查詢並等待結果,將查詢結果存儲在 result 變量中。

4. 檢查結果是否為空:

if (isEmpty(result)) return null;
  • 使用 isEmpty 函數檢查結果是否為空。
  • 如果結果為空,返回 null

5. 轉換並返回結果:

return this.DBData2DataObject(result[0]) as T;
  • 如果結果不為空,將結果中的第一條記錄轉換為資料物件,這是通過 DBData2DataObject 方法來完成的。
  • 將轉換後的結果以型別 T 返回,這意味著返回的物件具有型別 T

※ 建立單筆記錄:

public create = async (data: Omit<T, 'id'>, trx?: Knex.Transaction) => {
let sqlBuilder = this.knexSql(this.tableName).insert(this.DataObject2DBData(data));

if (trx) sqlBuilder = sqlBuilder.transacting(trx);

const result = await sqlBuilder;

if (isEmpty(result)) return null;
const id = result[0];
return await this.findOne(id, trx);

}

程式碼解說:

  1. 方法定義
public create = async (data: Omit<T, 'id'>, trx?: Knex.Transaction) => {
  • data: Omit<T, 'id'>:傳入的資料不包含 id 屬性,類型為 T(除了 id)。
  1. 建立 SQL 插入查詢
let sqlBuilder = this.knexSql(this.tableName).insert(this.DataObject2DBData(data));
  • 使用 Knex.js 建立一個插入查詢,將資料插入到 this.tableName 表中,資料經過 this.DataObject2DBData(data) 處理。
  1. 結果處理
if (isEmpty(result)) return null;
const id = result[0];
return await this.findOne(id, trx);
    • 如果有結果,提取插入記錄的 id,並使用 findOne 方法根據 id 查詢並返回新創建的記錄。

    ※ 更新單筆記錄:

    public update = async (id: any, data: Partial<Omit<T, 'id'>>, trx?: Knex.Transaction) => {
    let sqlBuilder = this.knexSql(this.tableName).update(this.DataObject2DBData(data).where({ id }));

    if (trx) sqlBuilder = sqlBuilder.transacting(trx);

    await sqlBuilder;

    return await this.findOne(id, trx);}

    程式碼解說:

    1. 建構更新查詢:
    let sqlBuilder = this.knexSql(this.tableName).update(this.DataObject2DBData(data)).where({ id });
    • 使用 knexSql 方法來構建更新查詢,指定更新的表 this.tableName
    • 使用 this.DataObject2DBData(data) 方法將 data 轉換為適合資料庫的格式。
    • 使用 where 方法指定更新條件,即 id
    2. 檢查並處理交易:
    if (trx) sqlBuilder = sqlBuilder.transacting(trx);
    如果提供了交易 trx,則使用 transacting 方法將查詢包含在交易中。3. 執行更新查詢:
    await sqlBuilder;
    執行更新查詢,等待更新操作完成。4. 查詢並返回更新後的記錄:
    return await this.findOne(id, trx);
    使用 findOne 方法根據 id 查詢並返回更新後的記錄,並傳遞可選的交易 trx

    ※ 刪除單筆記錄:

    public delete = async (id: any, trx?: Knex.Transaction) => {
    let sqlBuilder = this.knexSql(this.tableName).where({ id }).del();

    if (trx) sqlBuilder = sqlBuilder.transacting(trx);

    await sqlBuilder;

    return

    }
    程式碼解說:
    return;
    • 方法完成後不返回任何數據,因為這個方法的主要目的是刪除指定記錄。



※  建立產品檔案 :Model -->product.ts

raw-image

※ 定義Production 的 Schema:

export interface Product {
//符合 TypeScript 的語法和類型系統
id: number;
name: string;
amount: number;
description: string;
pre_order: number;
price: number;
}

程式碼解說:

  • id: number;:產品的唯一標識符,類型為數字。
  • name: string;:產品名稱,類型為字串。
  • amount: number;:產品數量,類型為數字。
  • description: string;:產品描述,類型為字串。
  • pre_order: number;:預購數量,類型為數字。
  • price: number;:產品價格,類型為數字。

※ 定義Production 的 型別:

export interface IProductModel extends IBase<Product> {}

程式碼解說:

  1. export interface IProductModel extends IBase<Product> {}:
    • export:表示這個介面可以被其他模組引用。
    • interface IProductModel:定義了一個介面 IProductModel。
    • extends IBase<Product>:表示 IProductModel 繼承自 IBase<Product>。
  2. IBase<Product>:這表示 IProductModel 繼承了 IBase 介面的所有方法和屬性,而這些方法和屬性的泛型類型為 Product。這意味著 IProductModel 會擁有 IBase 定義的所有 CRUD 操作(如 findAll, findOne, create, update, delete),但這些操作針對的是 Product 類型的資料。

簡單來說,IProductModel 介面是 IBase 介面的具體化,專門用於操作 Product 類型的資料。這樣定義能讓代碼更具模組化和可重用性。

※ 實作class:

  1. 類別定義
export class ProductModel extends Base<Product> implements IProductModel {}
raw-image

程式碼解說:

  • export class ProductModel:定義並導出一個名為 ProductModel 的類別。
  • extends Base<Product>:表示這個類別繼承自 Base<Product> 類別。
  • implements IProductModel:表示這個類別實現了 IProductModel 介面。
  1. tableName 屬性:
tableName = 'products';

程式碼解說:

  • 定義資料表的名稱為 products。
  1. schema 屬性
schema = {
id: 'id',
name: 'name',
amount: 'amount',
description: 'description',
pre_order: 'pre_order',
price: 'price'
};

程式碼解說:

  • 定義資料表的結構,把資料物件的屬性對應到資料庫的欄位,其中value是資料庫裡面的欄位名稱。
  1. 建構函數
constructor({ knexSql, tableName }: { knexSql: Knex; tableName?: string }) {
super({ knexSql, tableName });
}
  • 接受一個包含 knexSql 和可選 tableName 的物件作為參數,並呼叫父類別的建構函數super),將這些參數傳遞給父類別進行初始化。

5.將資料表的名稱設定為 products:

tableName = "products";
  1. 靜態方法 createModel:專門用來生成模型(model)
static createModel = ({
knexSql,
tableName,
}: {
knexSql: Knex;
tableName?: string;
}) => {
return new ProductModel({ knexSql, tableName })
}

程式碼解說:

  • static:表示這是一個靜態方法,可以直接使用 ProductModel.createModel() 來調用。
  • createModel:方法名稱。
  • 參數:接受一個包含 knexSql 和可選 tableName 的物件。
  • 返回:創建並返回一個 ProductModel 的新實例,也就無需每次手動傳入參數來初始化它。

※ 建立一個管理者的資料夾,負責建立不同的管理 manager:

raw-image


※ 在manager資料夾中,建立modelManager.ts:

raw-image

※ 設定資料庫模型的管理:

  1. 匯入模組
import { IProductModel, ProductModel } from "@/model/products";

import { Knex } from "knex";



2.使用 models 進行資料庫操作:

export const modelManager = ({knexSql}: {knexSql: Knex}) => {

const productModel = ProductModel.createModel({knexSql});

return {productModel}

}

程式碼解說:

  • modelManagerFile 文件中導入 modelManager,並用配置好的 knex 物件初始化它,然後可以使用 models 進行各種資料庫操作。
  • ProductModel.createModel:這是一個靜態方法,用於創建 ProductModel 的實例。
  • 參數 { knexSql }:將 knexSql 物件傳遞給 createModel 方法,這通常是資料庫連接的配置。

3.定義一個模型相關的環境資訊:

export interface ModelContext {

productModel: IProductModel;

}

程式碼解說:

  • 接口定義接口用於描述物件的結構和類型。
  • 導出export 關鍵字允許這個接口在其他文件中被導入使用。
  • productModel 必須符合 IProductModel 介面所定義的結構和型別。

※ 調用 modelManager 函數:src --> app.ts

raw-image

在app.ts新增程式碼:

import { ModelContext, modelManager } from './manager/modelManager';//新增

class App {

private modelCtx: ModelContext;//新增

constructor() {



this.modelCtx = modelManager({ knexSql: this.knexSql });//新增



}
  1. 匯入模組
import { ModelContext, modelManager } from './manager/modelManager'; 

程式碼解說:

目的是從指定的路徑(./manager/modelManager)匯入兩個模組:ModelContext 和 modelManager。

  • ModelContext:這是一個介面,用來描述資料模型上下文的結構和類型。
  • modelManager:這是一個函數,用來初始化和管理資料模型,並返回一個包含所有資料模型實例的物件。

2. 類別定義:

class App {

private modelCtx: ModelContext;

程式碼解說:

目的是在 App 類別中定義一個私有屬性 modelCtx,它的類型為 ModelContext。主要用途:

  • 強制型別:透過將 modelCtx 的類型設定為 ModelContext,可以確保 modelCtx 符合特定的結構和類型要求,這有助於在編譯時期捕捉潛在的錯誤。
  • 資料模型管理:modelCtx 用來存儲資料模型的上下文,這樣可以方便地在應用程式的其他地方訪問和操作這些資料模型。
  • 封裝性:private 關鍵字表示 modelCtx 屬性只能在 App 類別內部訪問,這增加了程式碼的封裝性和安全性,防止外部未經授權的訪問和修改。
  • "Ctx" 是 "Context" 的縮寫。通常用來表示一個上下文或環境,用來存儲和共享關鍵資料和設定。

3.建構函數

constructor() {

this.modelCtx = modelManager({ knexSql: this.knexSql });

}

程式碼解說:

目的是在 App 類別的建構函數中初始化資料模型上下文。

  • 呼叫 modelManager 函數,並將 knexSql 資料庫連接實例傳遞給它。
  • modelManager 函數會根據傳入的 knexSql 初始化資料模型,並返回包含這些模型的上下文物件。
  • 這個上下文物件被儲存在 this.modelCtx 屬性中。

※ 指定 modelManager 函數的返回值類型:manager --> modelManager.ts

export const modelManager = ({ knexSql }: { knexSql: Knex }): ModelContext => {



}

程式碼解說:

ModelContext 是為了指定 modelManager 函數的返回值類型。







全端網頁開發專業知識分享
留言0
查看全部
avatar-img
發表第一個留言支持創作者!
※ 認識Knex.js: Knex.js是一個專門串接資料庫的抽象化層,它支援多種關聯式資料庫,包括 PostgreSQL、MySQL、MariaDB、SQLite3、Oracle 和 Amazon Redshift 等。好處是連接以上的資料庫時,可以直接使用Knex.js的語法,就會自動創建出
※ 架構設計 ※ 資料庫規劃 id:流水號,唯一代替產品名稱的辨識代碼,AUTO_INCREMENT。 name:VARCHAR(255),NOT NULL。 amount:INTEGER(整數),UNSIGNED(不能是負數)。 description:TEXT,描述產品。 pre_
※ 下載Typescript 建立 TypeScript 和 Express 的基本目錄結構和必要配置。 npm install -g typescript-express-generator 建立模板引擎: ts-express --view=ejs server 安裝node_m
※ 必備開發環境: Node.Js環境。 Npm或yarn套件管理工具。 Docker倉庫套件:可以快速建立MySQL的資料庫。 下載 Docker Desktop:Docker Desktop for Windows。 ※ Docker快速建立MySQL資料庫,使用步驟: 準備Dock
※ 開發第三方金流串接使用工具: Typescript。 MySQL。 ※ 開發第三方金流串接使用技術: 基礎的docker使用方式。 用Typescript開發Node.JS的金流服務伺服器。 前端框架VueJs。 ※ 常見第三方金流平台介紹: PayPal: 特色: 全球最大
※ 具備三項工具: 編輯器 終端機 瀏覽器 ※ 步驟一:建立資料夾 mkdir mongodb-demo cd mongodb-demo code . ※ 步驟二:初始化,建立package.json npm init -y ※ 步驟三:安裝網頁伺服器 – Express 因為
※ 認識Knex.js: Knex.js是一個專門串接資料庫的抽象化層,它支援多種關聯式資料庫,包括 PostgreSQL、MySQL、MariaDB、SQLite3、Oracle 和 Amazon Redshift 等。好處是連接以上的資料庫時,可以直接使用Knex.js的語法,就會自動創建出
※ 架構設計 ※ 資料庫規劃 id:流水號,唯一代替產品名稱的辨識代碼,AUTO_INCREMENT。 name:VARCHAR(255),NOT NULL。 amount:INTEGER(整數),UNSIGNED(不能是負數)。 description:TEXT,描述產品。 pre_
※ 下載Typescript 建立 TypeScript 和 Express 的基本目錄結構和必要配置。 npm install -g typescript-express-generator 建立模板引擎: ts-express --view=ejs server 安裝node_m
※ 必備開發環境: Node.Js環境。 Npm或yarn套件管理工具。 Docker倉庫套件:可以快速建立MySQL的資料庫。 下載 Docker Desktop:Docker Desktop for Windows。 ※ Docker快速建立MySQL資料庫,使用步驟: 準備Dock
※ 開發第三方金流串接使用工具: Typescript。 MySQL。 ※ 開發第三方金流串接使用技術: 基礎的docker使用方式。 用Typescript開發Node.JS的金流服務伺服器。 前端框架VueJs。 ※ 常見第三方金流平台介紹: PayPal: 特色: 全球最大
※ 具備三項工具: 編輯器 終端機 瀏覽器 ※ 步驟一:建立資料夾 mkdir mongodb-demo cd mongodb-demo code . ※ 步驟二:初始化,建立package.json npm init -y ※ 步驟三:安裝網頁伺服器 – Express 因為
你可能也想看
Google News 追蹤
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
Thumbnail
※ 視圖模板 視圖模板(View Templates) 是在 MVC 架構中負責展示數據的 HTML 文件,包含模板語法,用於在渲染時插入實際數據。它們的主要目的是分離數據與展示邏輯,讓代碼更加模塊化和易於維護。 視圖模板設計和使用的核心理念,就是「重複的事情不要重複做、效益最大化、有效利用資源
Thumbnail
在 TypeScript 中,套件是模組化代碼的集合,可以提高代碼的可重用性和可維護性。常見的套件包括各種庫和框架,如 lodash、express 等。以下是有關引用套件、自定義套件和常見套件的詳細介紹。
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
Thumbnail
TypeScript是一種由Microsoft開發和維護的開源編程語言。它是JavaScript的超集,主要擴展了JavaScript的語法,增加了靜態類型檢查和其他特性,使得開發大型應用程序更為方便和可靠。
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
題目敘述 題目會給我們一組定義好的界面和需求,要求我們設計一個資料結構,可以滿足平均O(1)的插入元素、刪除元素、隨機取得元素的操作。 RandomizedSet() 類別建構子 bool insert(int val) 插入元素的function界面 bool remove(int val
Thumbnail
嘿,大家新年快樂~ 新年大家都在做什麼呢? 跨年夜的我趕工製作某個外包設計案,在工作告一段落時趕上倒數。 然後和兩個小孩過了一個忙亂的元旦。在深夜時刻,看到朋友傳來的解籤網站,興致勃勃熬夜體驗了一下,覺得非常好玩,或許有人玩過了,但還是想寫上來分享紀錄一下~
Thumbnail
一般在使用 TypeScript 的時候,大家都有遇過定義列舉資料的情境吧。 不過不管是 enum 和 literal 的方式其實都有些小缺點,以下推薦一個個人認為體驗更好的方式。
Thumbnail
※ 原本狀態:伺服器渲染 這是 MVC 架構下的 request / response 示意圖,在這張圖呈現的架構裡,畫面和資料都由同一個架構處理。 伺服器渲染流程: 瀏覽器針對特定網址送出請求。 路由器解析請求後,轉接給對應的 controller。 controller 按照要求,透過
Thumbnail
※ 視圖模板 視圖模板(View Templates) 是在 MVC 架構中負責展示數據的 HTML 文件,包含模板語法,用於在渲染時插入實際數據。它們的主要目的是分離數據與展示邏輯,讓代碼更加模塊化和易於維護。 視圖模板設計和使用的核心理念,就是「重複的事情不要重複做、效益最大化、有效利用資源
Thumbnail
在 TypeScript 中,套件是模組化代碼的集合,可以提高代碼的可重用性和可維護性。常見的套件包括各種庫和框架,如 lodash、express 等。以下是有關引用套件、自定義套件和常見套件的詳細介紹。
Thumbnail
本章節旨在介紹TypeScript的基本語法,包括一般結構、程式進入點、註解以及變數的定義和賦值。這些知識將幫助讀者瞭解TypeScript的基本架構,並且可以開始使用TypeScript進行開發。
Thumbnail
TypeScript是一種由Microsoft開發和維護的開源編程語言。它是JavaScript的超集,主要擴展了JavaScript的語法,增加了靜態類型檢查和其他特性,使得開發大型應用程序更為方便和可靠。
Thumbnail
因為最近想嘗試編碼風格,於是就選了一套比較"不嚴格"的輔助工具來摸索。 編輯器 VS CODE 框架 VUE3 打包工具 VITE 編碼風格 Standard 環境 version { "nodejs":"v18.18.0", "npm":"9.8.1" }
Thumbnail
題目敘述 題目會給我們一組定義好的界面和需求,要求我們設計一個資料結構,可以滿足平均O(1)的插入元素、刪除元素、隨機取得元素的操作。 RandomizedSet() 類別建構子 bool insert(int val) 插入元素的function界面 bool remove(int val