From 9c234009d45d40b8bf75be515eff03d4a6ce4419 Mon Sep 17 00:00:00 2001 From: kennethcheng Date: Mon, 27 Apr 2026 19:55:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(api):=20=E5=BB=BA=E7=AB=8B=20transactions?= =?UTF-8?q?=20=E7=9A=84=20Server=20Actions=EF=BC=8C=E5=AE=9E=E8=A3=85?= =?UTF-8?q?=E9=AB=98=E7=B2=BE=E5=BA=A6=E5=AD=97=E7=AC=A6=E4=B8=B2=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/actions/transaction.ts | 50 ++++++++++++++++++++++++++++++++++++++ src/db/schema.ts | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 src/actions/transaction.ts diff --git a/src/actions/transaction.ts b/src/actions/transaction.ts new file mode 100644 index 0000000..24ac31f --- /dev/null +++ b/src/actions/transaction.ts @@ -0,0 +1,50 @@ +'use server'; + +import { db } from '@/db'; +import { transactions, transactionTypeEnum } from '@/db/schema'; +import { z } from 'zod'; +import { eq, desc } from 'drizzle-orm'; + +const createTransactionSchema = z.object({ + assetId: z.string().uuid(), + txType: z.enum(['BUY', 'SELL', 'DIVIDEND', 'AIRDROP', 'FEE']), + quantity: z.string().regex(/^-?\d+(\.\d+)?$/, '数量必须是数字字符串'), + price: z.string().regex(/^-?\d+(\.\d+)?$/, '价格必须是数字字符串'), + fee: z.string().regex(/^-?\d+(\.\d+)?$/, '手续费必须是数字字符串').default('0'), + txCurrency: z.string().min(1), + exchangeRate: z.string().regex(/^-?\d+(\.\d+)?$/, '汇率必须是数字字符串').default('1'), + executedAt: z.coerce.date(), +}); + +export async function createTransaction(params: z.infer) { + const validation = createTransactionSchema.safeParse(params); + if (!validation.success) { + return { success: false, error: validation.error.issues[0].message }; + } + + try { + const [transaction] = await db.insert(transactions).values(validation.data).returning(); + return { success: true, data: transaction }; + } catch (error: unknown) { + if ( + error && + typeof error === 'object' && + 'code' in error && + (error as { code: string }).code === '23503' + ) { + return { success: false, error: '资产不存在' }; + } + throw error; + } +} + +export async function getTransactions(assetId?: string) { + if (assetId) { + return db + .select() + .from(transactions) + .where(eq(transactions.assetId, assetId)) + .orderBy(desc(transactions.executedAt)); + } + return db.select().from(transactions).orderBy(desc(transactions.executedAt)); +} \ No newline at end of file diff --git a/src/db/schema.ts b/src/db/schema.ts index 76d5d60..0ce46b5 100644 --- a/src/db/schema.ts +++ b/src/db/schema.ts @@ -33,7 +33,7 @@ export const transactionTypeEnum = pgEnum("transaction_type_enum", [ ]); export const transactions = pgTable("transactions", { - id: uuid("id").primaryKey(), + id: uuid("id").primaryKey().defaultRandom(), assetId: uuid("asset_id") .notNull() .references(() => assets.id),