From e13b25ffcddcc93da6b21bf803dfcafe80ad55c6 Mon Sep 17 00:00:00 2001 From: ddmt Date: Mon, 2 Feb 2026 14:43:39 +0800 Subject: [PATCH] init --- .gitignore | 34 +++++++++ api.md | 193 ++++++++++++++++++++++++++++++++++++++++++++++++++ bun.lock | 26 +++++++ index.ts | 181 ++++++++++++++++++++++++++++++++++++++++++++++ install.sql | 26 +++++++ orders.db | Bin 0 -> 12288 bytes package.json | 16 +++++ tsconfig.json | 29 ++++++++ 8 files changed, 505 insertions(+) create mode 100644 .gitignore create mode 100644 api.md create mode 100644 bun.lock create mode 100644 index.ts create mode 100644 install.sql create mode 100644 orders.db create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a14702c --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store diff --git a/api.md b/api.md new file mode 100644 index 0000000..6b9add5 --- /dev/null +++ b/api.md @@ -0,0 +1,193 @@ +# API 接口文檔 + +## 訂單管理系統 API 接口文檔 + +### 1. 添加訂單 + +#### 接口信息 +- **接口路徑**: `POST /api/orders` +- **功能描述**: 添加新的訂單記錄 +- **請求格式**: JSON +- **響應格式**: JSON + +#### 請求參數 +| 參數名 | 類型 | 必填 | 說明 | +|--------|------|------|------| +| customer_name | String | 是 | 客戶姓名 | +| product_name | String | 是 | 商品名稱 | +| quantity | Number | 是 | 商品數量,必須大於0 | +| price | Number | 是 | 商品單價,必須大於0 | +| status | String | 否 | 訂單狀態,默認為"pending" | + +#### 響應參數 +| 參數名 | 類型 | 說明 | +|--------|------|------| +| success | Boolean | 請求是否成功 | +| message | String | 響應消息 | +| data | Object | 返回的數據對象 | +| data.id | Number | 新增訂單的ID | +| data.customer_name | String | 客戶姓名 | +| data.product_name | String | 商品名稱 | +| data.quantity | Number | 商品數量 | +| data.price | Number | 商品單價 | +| data.status | String | 訂單狀態 | +| data.created_at | String | 創建時間 | + +#### 成功響應示例 +```json +{ + "success": true, + "message": "訂單創建成功", + "data": { + "id": 6, + "customer_name": "陳八", + "product_name": "HomePod Mini", + "quantity": 1, + "price": 99.99, + "status": "pending", + "created_at": "2023-12-01 10:30:00" + } +} +``` + +#### 失敗響應示例 +```json +{ + "success": false, + "message": "缺少必需字段或字段值無效" +} +``` + +#### Curl 請求示例 +```bash +curl -X POST http://localhost:3000/api/orders \ + -H "Content-Type: application/json" \ + -H "Accept: application/json" \ + -d '{ + "customer_name": "陳八", + "product_name": "HomePod Mini", + "quantity": 1, + "price": 99.99, + "status": "pending" + }' +``` + +### 2. 查詢訂單列表 + +#### 接口信息 +- **接口路徑**: `GET /api/orders` +- **功能描述**: 查詢訂單列表,支持條件篩選和分頁 +- **請求格式**: Query String +- **響應格式**: JSON + +#### 查詢參數 +| 參數名 | 類型 | 必填 | 默認值 | 說明 | +|--------|------|------|--------|------| +| page | Number | 否 | 1 | 頁碼 | +| pageSize | Number | 否 | 10 | 每頁顯示數量 | +| customerName | String | 否 | - | 客戶姓名模糊匹配 | +| productName | String | 否 | - | 商品名稱模糊匹配 | +| status | String | 否 | - | 訂單狀態精確匹配 | + +#### 響應參數 +| 參數名 | 類型 | 說明 | +|--------|------|------| +| success | Boolean | 請求是否成功 | +| data | Object | 返回的數據對象 | +| data.orders | Array | 訂單列表 | +| data.pagination | Object | 分頁信息 | +| data.pagination.page | Number | 當前頁碼 | +| data.pagination.pageSize | Number | 每頁顯示數量 | +| data.pagination.total | Number | 總記錄數 | +| data.pagination.totalPages | Number | 總頁數 | + +#### 成功響應示例 +```json +{ + "success": true, + "data": { + "orders": [ + { + "id": 1, + "customer_name": "張三", + "product_name": "iPhone 15", + "quantity": 1, + "price": 999.99, + "status": "completed", + "created_at": "2023-12-01 10:30:00" + }, + { + "id": 2, + "customer_name": "李四", + "product_name": "MacBook Pro", + "quantity": 1, + "price": 1999.99, + "status": "pending", + "created_at": "2023-12-01 10:25:00" + } + ], + "pagination": { + "page": 1, + "pageSize": 10, + "total": 2, + "totalPages": 1 + } + } +} +``` + +#### Curl 請求示例 + +##### 查詢所有訂單(默認分頁) +```bash +curl "http://localhost:3000/api/orders" +``` + +##### 分頁查詢 +```bash +curl "http://localhost:3000/api/orders?page=1&pageSize=5" +``` + +##### 按客戶姓名查詢 +```bash +curl "http://localhost:3000/api/orders?customerName=張" +``` + +##### 按商品名稱查詢 +```bash +curl "http://localhost:3000/api/orders?productName=iPhone" +``` + +##### 按狀態查詢 +```bash +curl "http://localhost:3000/api/orders?status=pending" +``` + +##### 組合條件查詢 +```bash +curl "http://localhost:3000/api/orders?page=1&pageSize=10&customerName=張&status=completed" +``` + +### 3. 錯誤碼說明 + +| 錯誤碼 | 狀態碼 | 說明 | +|--------|--------|------| +| 400 | 400 Bad Request | 請求參數錯誤 | +| 404 | 404 Not Found | 路由不存在 | +| 500 | 500 Internal Server Error | 服務器內部錯誤 | + +### 4. 公共響應格式 + +所有接口響應都遵循以下格式: + +```json +{ + "success": true/false, + "message": "響應消息", + "data": {} +} +``` + +- `success`: 請求是否成功的布爾值 +- `message`: 響應消息,成功時可選,失敗時必填 +- `data`: 返回的業務數據,根據接口不同而異 \ No newline at end of file diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..3c58493 --- /dev/null +++ b/bun.lock @@ -0,0 +1,26 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "bun_lite", + "devDependencies": { + "@types/bun": "latest", + }, + "peerDependencies": { + "typescript": "^5", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.3.8", "", { "dependencies": { "bun-types": "1.3.8" } }, "sha512-3LvWJ2q5GerAXYxO2mffLTqOzEu5qnhEAlh48Vnu8WQfnmSwbgagjGZV6BoHKJztENYEDn6QmVd949W4uESRJA=="], + + "@types/node": ["@types/node@25.2.0", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w=="], + + "bun-types": ["bun-types@1.3.8", "", { "dependencies": { "@types/node": "*" } }, "sha512-fL99nxdOWvV4LqjmC+8Q9kW3M4QTtTR1eePs94v5ctGqU8OeceWrSUaRw3JYb7tU3FkMIAjkueehrHPPPGKi5Q=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="], + } +} diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..d356f03 --- /dev/null +++ b/index.ts @@ -0,0 +1,181 @@ +import { Database } from "bun:sqlite"; + +// 初始化 SQLite 數據庫 +const db = new Database("orders.db"); + +// 創建 orders 表 +db.run(` + CREATE TABLE IF NOT EXISTS orders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + customer_name TEXT NOT NULL, + product_name TEXT NOT NULL, + quantity INTEGER NOT NULL, + price REAL NOT NULL, + status TEXT DEFAULT 'pending', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ) +`); + +// 定義類型 +interface Order { + id?: number; + customer_name: string; + product_name: string; + quantity: number; + price: number; + status?: string; + created_at?: string; +} + +// 準備 SQL 語句 +const insertOrderStmt = db.prepare(` + INSERT INTO orders (customer_name, product_name, quantity, price, status) + VALUES ($customerName, $productName, $quantity, $price, $status) +`); + +const getOrdersStmt = db.prepare(` + SELECT * FROM orders + WHERE ($customerName IS NULL OR customer_name LIKE $customerName) + AND ($productName IS NULL OR product_name LIKE $productName) + AND ($status IS NULL OR status = $status) + ORDER BY created_at DESC + LIMIT $limit OFFSET $offset +`); + +const getOrderCountStmt = db.prepare(` + SELECT COUNT(*) as count FROM orders + WHERE ($customerName IS NULL OR customer_name LIKE $customerName) + AND ($productName IS NULL OR product_name LIKE $productName) + AND ($status IS NULL OR status = $status) +`); + +// 創建服務器 +const server = Bun.serve({ + port: 3000, + async fetch(req) { + const url = new URL(req.url); + const path = url.pathname; + const method = req.method; + + // CORS headers + const headers = { + "Content-Type": "application/json", + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE, OPTIONS", + "Access-Control-Allow-Headers": "Content-Type", + }; + + // 處理預檢請求 + if (method === "OPTIONS") { + return new Response(null, { status: 204, headers }); + } + + try { + if (path === "/api/orders" && method === "GET") { + // 解析查詢參數 + const page = parseInt(url.searchParams.get("page") || "1"); + const pageSize = parseInt(url.searchParams.get("pageSize") || "10"); + const customerName = url.searchParams.get("customerName") || null; + const productName = url.searchParams.get("productName") || null; + const status = url.searchParams.get("status") || null; + + // 計算偏移量 + const offset = (page - 1) * pageSize; + + // 準備查詢參數 + const params: any = { + $customerName: customerName ? `%${customerName}%` : null, + $productName: productName ? `%${productName}%` : null, + $status: status, + $limit: pageSize, + $offset: offset + }; + + // 查詢訂單列表 + const orders = getOrdersStmt.all(params) as Order[]; + + // 獲取總數 + const countResult: any = getOrderCountStmt.get({ + $customerName: customerName ? `%${customerName}%` : null, + $productName: productName ? `%${productName}%` : null, + $status: status + }); + + const total = countResult.count; + + return Response.json({ + success: true, + data: { + orders, + pagination: { + page, + pageSize, + total, + totalPages: Math.ceil(total / pageSize) + } + } + }, { headers }); + } else if (path === "/api/orders" && method === "POST") { + // 添加新訂單 + const body: any = await req.json(); + + const order: Order = { + customer_name: body.customer_name, + product_name: body.product_name, + quantity: body.quantity, + price: body.price, + status: body.status || 'pending' + }; + + // 驗證必需字段 + if (!order.customer_name || !order.product_name || order.quantity <= 0 || order.price <= 0) { + return Response.json({ + success: false, + message: "缺少必需字段或字段值無效" + }, { + status: 400, + headers + }); + } + + // 插入數據 + const result = insertOrderStmt.run({ + $customerName: order.customer_name, + $productName: order.product_name, + $quantity: order.quantity, + $price: order.price, + $status: order.status || 'pending' + }); + + return Response.json({ + success: true, + message: "訂單創建成功", + data: { + id: result.lastInsertRowid as number, + ...order + } + }, { headers }); + } else { + return Response.json({ + success: false, + message: "路由不存在" + }, { + status: 404, + headers + }); + } + } catch (error: any) { + console.error("服務器錯誤:", error); + + return Response.json({ + success: false, + message: error.message || "服務器內部錯誤" + }, { + status: 500, + headers + }); + } + } +}); + +console.log(`Server running at http://localhost:${server.port}`); \ No newline at end of file diff --git a/install.sql b/install.sql new file mode 100644 index 0000000..5645463 --- /dev/null +++ b/install.sql @@ -0,0 +1,26 @@ +-- SQLite 數據庫初始化腳本 +-- 創建 orders 表 + +CREATE TABLE IF NOT EXISTS orders ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + customer_name TEXT NOT NULL, + product_name TEXT NOT NULL, + quantity INTEGER NOT NULL, + price REAL NOT NULL, + status TEXT DEFAULT 'pending', + created_at DATETIME DEFAULT CURRENT_TIMESTAMP +); + +-- 插入示例數據 +INSERT INTO orders (customer_name, product_name, quantity, price, status) VALUES +('張三', 'iPhone 15', 1, 999.99, 'completed'), +('李四', 'MacBook Pro', 1, 1999.99, 'pending'), +('王五', 'iPad Air', 2, 599.99, 'shipped'), +('趙六', 'Apple Watch', 1, 399.99, 'delivered'), +('錢七', 'AirPods Pro', 1, 249.99, 'processing'); + +-- 創建索引以提高查詢性能 +CREATE INDEX IF NOT EXISTS idx_customer_name ON orders(customer_name); +CREATE INDEX IF NOT EXISTS idx_product_name ON orders(product_name); +CREATE INDEX IF NOT EXISTS idx_status ON orders(status); +CREATE INDEX IF NOT EXISTS idx_created_at ON orders(created_at); \ No newline at end of file diff --git a/orders.db b/orders.db new file mode 100644 index 0000000000000000000000000000000000000000..4faa860c1b872100cc2d091aceac54a7d722b44d GIT binary patch literal 12288 zcmeI&F>ljA6bJA-2T_Vd%YalxUEWf)r3In{W$08lJs`qKgL{$46q)!6mKvwA&jV~l z2(d5$Ujc!H7!eX5gHOQF!WZbm0xxxgL?pz(Qi=bQ&-R_)-T8joS>C?mMb^;HAn6B| zD&nG$QmhjRAsj9RF5{4Vb7oxO>!|*>aKzQ;-!E{MGqW#dKJy6y0SG_<0uX=z1Rwwb z2tWV=|EEC9IXkzqB0Dy?w`j>jA%p-IyG?EE%D-bPF3i6aWB-qh-*@~P$58?N7>+p0ruTW{5y+^wOS zdi6N7m!)>lH%WIiBWZP4Q*(<;+w<~-y<`w(J^RP!!z_qxWFP*TaI$;UGsH{vCdN%| zV6!xzaZ}xJ+n%PyJrjpfe1Gxe-4YX66LxtozsXd(-cU47t+joh7ttLxZfm#E;?*pV z_U3Z-li&jZ0SG_<0uX=z1Rwwb2tWV=5P-nx6R62^Vqrl}%~uXTzWes}_1Bkg4xfF< z?+;!*J$SM2tUvhi{?el_w@yA1luPC6l~S1xDpl7i*VoF`eE)wWW{*za0`W!=fB*y_ d009U<00Izz00bZa0SNqqz_ctBr}MubegdX2pPc{z literal 0 HcmV?d00001 diff --git a/package.json b/package.json new file mode 100644 index 0000000..42bdc2c --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "bun_lite", + "module": "index.ts", + "type": "module", + "private": true, + "scripts": { + "dev": "bun run index.ts", + "start": "bun run index.ts" + }, + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..bfa0fea --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + // Environment setup & latest features + "lib": ["ESNext"], + "target": "ESNext", + "module": "Preserve", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}