在前幾天的開發中,我們已經搭好了資料庫 Schema、API 邏輯以及自動化測試。但空空如有的資料庫就像是沒有顧客的百貨公司——你看不出效能瓶頸,也無法在 UI 開發時想像真實的使用情境。
今天我們的任務是:利用 AI 快速生成大量、真實且具備關聯性的 Mock Data(模擬資料),讓我們的財務追蹤器瞬間填充滿滿的消費紀錄,為後續的前端 UI 頁面開發做好準備!
要生成「好用」的 Mock Data,最忌諱給出模糊的指令。如果只跟 AI 說:「請幫我生成 50 筆記帳資料」,你只會得到一堆重複且毫無意義的文字。
我們需要 AI 結合 Faker.js 套件與 Prisma 的語法,產生出符合台灣在地消費情境的假資料:
你現在是一位 senior 後端工程師。請幫我撰寫一個 Prisma 的 Seed Script (prisma/seed.ts)。
需求如下:
1. 使用 `@faker-js/faker` 生成資料。
2. 先建立 1 個測試使用者 (User)。
3. 替該使用者生成 50 筆交易紀錄 (Transaction)。
4. 交易類型 (type) 包含 "INCOME" 與 "EXPENSE"。
5. 分類 (category) 請涵蓋日常消費(如:外食、交通、娛樂、薪水)。
6. 金額 (amount) 請符合合理台幣數字(例如外食 80~300 元,薪水 50000 元)。
7. 時間 (date) 隨機分佈在過去 30 天內。
AI 很快地給出了一份 prisma/seed.ts 草稿:
import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';
const prisma = new PrismaClient();
async function main() {
// 清除舊資料
await prisma.transaction.deleteMany();
await prisma.user.deleteMany();
// 建立預設使用者
const user = await prisma.user.create({
data: {
email: 'demo@example.com',
name: 'Test User',
},
});
const categories = ['外食', '交通', '娛樂', '薪水'];
// 生成 50 筆資料
for (let i = 0; i < 50; i++) {
const isIncome = faker.datatype.boolean();
await prisma.transaction.create({
data: {
userId: user.id,
amount: isIncome ? 50000 : faker.number.int({ min: 80, max: 300 }),
type: isIncome ? 'INCOME' : 'EXPENSE',
category: isIncome ? '薪水' : faker.helpers.arrayElement(categories.slice(0, 3)),
note: faker.lorem.sentence(),
date: faker.date.recent({ days: 30 }),
},
});
}
}
main()
.catch((e) => console.error(e))
.finally(async () => await prisma.$disconnect());
這段程式碼看起來很完美,但一執行 npx prisma db seed 就踩坑了!
AI 使用了最直觀的 for 迴圈搭配 await prisma.transaction.create()。這意味著程式發送了 50 次獨立的資料庫 Request!雖然 50 筆感覺很快,但如果未來要測試 5,000 筆 Big Data 效能時,這會讓 Seed 過程花費數分鐘。
faker.datatype.boolean() 的機率是 50%/50%。這會導致生成出來的 50 筆紀錄裡有 25 筆都是「薪水」,這哪是什麼個人財務追蹤器,根本是發暴財的假象!
我們引導 AI 進行二次重構:
prisma.transaction.createMany()** 進行批次插入(Batch Insert),提升 10 倍速度。faker.lorem.sentence() 會生成拉丁文無意義句子,改成在地化的中文備註(如「午餐便當」、「捷運儲值」)。修正後的精華程式碼:
// 建立批次陣列
const transactionsData = Array.from({ length: 50 }).map(() => {
// 95% 概率為支出
const isExpense = faker.number.float({ min: 0, max: 1 }) < 0.95;
return {
userId: user.id,
amount: isExpense
? faker.number.int({ min: 60, max: 1500 })
: faker.number.int({ min: 30000, max: 60000 }),
type: isExpense ? 'EXPENSE' : 'INCOME',
category: isExpense ? faker.helpers.arrayElement(['外食', '交通', '購物']) : '薪水',
note: isExpense ? faker.helpers.arrayElement(['便利商店', '午餐搭計程車', '星巴克咖啡']) : '月薪發放',
date: faker.date.recent({ days: 30 }),
};
});
// 一次性高效插入
await prisma.transaction.createMany({
data: transactionsData,
});
await 會強制程式進入序列化執行(Sequential execution)。當我們處理大量資料注入時,優先考慮將資料集中至記憶體陣列,再透過批次寫入(createMany)來大幅減少 I/O 開銷。Faker.js 的 API 語法,但它缺乏對真實業務邏輯(Business Domain Logic)的敏銳度(例如收支比例、合理消費金額)。工程師的核心價值就在於「輸入正確的世界觀」,引導 AI 吐出符合真實狀況的資料。