This commit is contained in:
2026-02-02 14:43:39 +08:00
parent c85c6315dc
commit e13b25ffcd
8 changed files with 505 additions and 0 deletions

34
.gitignore vendored Normal file
View File

@@ -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

193
api.md Normal file
View File

@@ -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`: 返回的業務數據,根據接口不同而異

26
bun.lock Normal file
View File

@@ -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=="],
}
}

181
index.ts Normal file
View File

@@ -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}`);

26
install.sql Normal file
View File

@@ -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);

BIN
orders.db Normal file

Binary file not shown.

16
package.json Normal file
View File

@@ -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"
}
}

29
tsconfig.json Normal file
View File

@@ -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
}
}