"use server"; import { z } from "zod"; import { and, desc, eq, isNull } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { auth } from "@/lib/auth"; import { db } from "@/lib/db"; import { accounts, channels, type Account } from "@/lib/db/schema"; async function requireUser() { const session = await auth(); if (!session?.user?.id) { throw new Error("请先登录"); } return session.user.id; } const accountSchema = z.object({ name: z.string().min(1, "请输入账户名称").max(100), accountType: z.enum(["BANK", "E_WALLET", "CASH"] as const), balanceType: z.enum(["ASSET", "LIABILITY", "EQUITY"] as const), primaryCurrency: z.string().trim().max(10).optional().nullable(), supportedCurrencies: z.array(z.string().trim()).optional().nullable(), remark: z.string().max(500).optional().nullable(), ext: z.string().optional().nullable(), // 银行账户字段 issuerName: z.string().max(100).optional().nullable(), accountNumber: z.string().max(100).optional().nullable(), // 电子钱包字段 platform: z.string().max(64).optional().nullable(), accountId: z.string().max(100).optional().nullable(), // 现金账户字段 location: z.string().max(150).optional().nullable(), }); export type AccountInput = z.infer; export type AccountWithChannels = Account & { channelCount: number; }; export async function getAccountsAction(): Promise<{ success: boolean; data?: AccountWithChannels[]; error?: string; }> { try { const userId = await requireUser(); const userAccounts = await db .select() .from(accounts) .where(and(eq(accounts.userId, userId), isNull(accounts.deletedAt))) .orderBy(desc(accounts.createdAt)); const userChannels = await db .select({ id: channels.id, refAccounts: channels.refAccounts }) .from(channels) .where(and(eq(channels.userId, userId), isNull(channels.deletedAt))); const result: AccountWithChannels[] = userAccounts.map((acc) => { const count = userChannels.filter((ch) => Array.isArray(ch.refAccounts) && ch.refAccounts.includes(acc.id) ).length; return { ...acc, channelCount: count, }; }); return { success: true, data: result }; } catch (err) { console.error("getAccountsAction error:", err); return { success: false, error: err instanceof Error ? err.message : "获取账户失败" }; } } export async function createAccountAction( data: AccountInput ): Promise<{ success: boolean; data?: Account; error?: string }> { try { const userId = await requireUser(); const parsed = accountSchema.safeParse(data); if (!parsed.success) { return { success: false, error: parsed.error.issues[0]?.message || "参数错误" }; } const val = parsed.data; const [newAccount] = await db .insert(accounts) .values({ userId, name: val.name.trim(), accountType: val.accountType, balanceType: val.balanceType, primaryCurrency: val.primaryCurrency ? val.primaryCurrency.toUpperCase() : null, supportedCurrencies: val.supportedCurrencies && val.supportedCurrencies.length > 0 ? val.supportedCurrencies : null, remark: val.remark?.trim() || null, ext: val.ext?.trim() || null, issuerName: val.issuerName?.trim() || null, accountNumber: val.accountNumber?.trim() || null, platform: val.platform?.trim() || null, accountId: val.accountId?.trim() || null, location: val.location?.trim() || null, isActive: true, }) .returning(); revalidatePath("/accounts"); revalidatePath("/channels"); revalidatePath("/"); return { success: true, data: newAccount }; } catch (err) { console.error("createAccountAction error:", err); return { success: false, error: err instanceof Error ? err.message : "创建账户失败" }; } } export async function updateAccountAction( id: string, data: AccountInput ): Promise<{ success: boolean; data?: Account; error?: string }> { try { const userId = await requireUser(); const parsed = accountSchema.safeParse(data); if (!parsed.success) { return { success: false, error: parsed.error.issues[0]?.message || "参数错误" }; } const val = parsed.data; const [updated] = await db .update(accounts) .set({ name: val.name.trim(), accountType: val.accountType, balanceType: val.balanceType, primaryCurrency: val.primaryCurrency ? val.primaryCurrency.toUpperCase() : null, supportedCurrencies: val.supportedCurrencies && val.supportedCurrencies.length > 0 ? val.supportedCurrencies : null, remark: val.remark?.trim() || null, ext: val.ext?.trim() || null, issuerName: val.issuerName?.trim() || null, accountNumber: val.accountNumber?.trim() || null, platform: val.platform?.trim() || null, accountId: val.accountId?.trim() || null, location: val.location?.trim() || null, updatedAt: new Date(), }) .where(and(eq(accounts.id, id), eq(accounts.userId, userId), isNull(accounts.deletedAt))) .returning(); if (!updated) { return { success: false, error: "未找到该账户或无权修改" }; } revalidatePath("/accounts"); revalidatePath("/channels"); revalidatePath("/"); return { success: true, data: updated }; } catch (err) { console.error("updateAccountAction error:", err); return { success: false, error: err instanceof Error ? err.message : "更新账户失败" }; } } export async function toggleAccountActiveAction( id: string, isActive: boolean ): Promise<{ success: boolean; error?: string }> { try { const userId = await requireUser(); await db .update(accounts) .set({ isActive, updatedAt: new Date(), }) .where(and(eq(accounts.id, id), eq(accounts.userId, userId), isNull(accounts.deletedAt))); revalidatePath("/accounts"); return { success: true }; } catch (err) { console.error("toggleAccountActiveAction error:", err); return { success: false, error: err instanceof Error ? err.message : "状态切换失败" }; } } export async function deleteAccountAction( id: string ): Promise<{ success: boolean; error?: string }> { try { const userId = await requireUser(); // 软删除 const [deleted] = await db .update(accounts) .set({ deletedAt: new Date(), updatedAt: new Date(), }) .where(and(eq(accounts.id, id), eq(accounts.userId, userId), isNull(accounts.deletedAt))) .returning(); if (!deleted) { return { success: false, error: "账户不存在或已删除" }; } revalidatePath("/accounts"); revalidatePath("/channels"); revalidatePath("/"); return { success: true }; } catch (err) { console.error("deleteAccountAction error:", err); return { success: false, error: err instanceof Error ? err.message : "删除账户失败" }; } }