"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 : "删除账户失败", } } }