当前位置: 首页 > news >正文

全栈开发实战:基于Next.js与SQLite构建个人收入追踪系统

1. 项目概述与核心价值

最近在独立开发者圈子里,一个叫“Indomi/earnings-tracker”的项目引起了我的注意。乍一看这个名字,你可能会觉得它又是一个平平无奇的收入追踪工具,但当你真正去拆解它的设计思路和代码实现时,会发现它精准地戳中了一个非常具体的痛点:为自由职业者、独立开发者、内容创作者等“一人公司”提供一套轻量、自动化、可私有化部署的收入与现金流管理方案

这个项目本质上是一个自托管的Web应用,它允许你将多个收入来源(比如GitHub Sponsors、Patreon、Stripe、PayPal,甚至手动录入的银行转账)的数据聚合到一个统一的仪表盘上。它的核心价值不在于做出多么复杂的财务分析,而在于解决“信息碎片化”的问题。我们这些靠手艺吃饭的人,收入渠道往往很分散,今天一笔咨询费到PayPal,明天一笔赞助到GitHub,月底可能还有平台分成。手动去各个平台查账、记Excel,不仅耗时,还容易遗漏。Earnings Tracker 通过API集成和手动补录相结合的方式,试图把这件事自动化,让你一眼就能看清“这个月到底赚了多少钱”,以及“钱都是从哪儿来的”。

我花了一些时间部署和测试了这个项目,它给我的感觉更像是一个“够用就好”的极客工具箱,而不是一个功能臃肿的企业级软件。它没有复杂的复式记账逻辑,没有处理税费的模块,它的目标非常单纯:记录流入的现金,并帮你归类。这种清晰的边界感,恰恰是很多个人开发者项目成功的关键——不做大而全,只解决一个核心问题,并且解决得足够好。接下来,我将从技术选型、实现细节、部署踩坑和扩展思路几个方面,完整地拆解这个项目。

1.1 核心需求与场景解析

为什么我们需要一个专门的“收入追踪器”,而不是直接用现成的会计软件或笔记?这得从目标用户——独立数字工作者的工作流说起。

核心场景一:多平台收入聚合。一个典型的全栈自由职业者,收入可能来自:Upwork或Toptal的项目佣金(通过Payoneer或PayPal)、个人网站接的私活(直接银行转账或Stripe)、在Gumroad上卖数字产品、开源项目获得的GitHub Sponsors或Open Collective赞助,以及偶尔的咨询费。这些资金散落在5-6个不同的金融科技平台,每个平台的报表格式、统计周期、币种都不同。每月底为了统计总收入,需要登录所有平台,导出CSV,手动换算汇率并加总,这个过程极其低效且易错。

核心场景二:现金流健康度感知。对于个人或微型工作室,现金流就是生命线。我们不仅需要知道总收入,更需要一种直观的方式感知收入趋势和稳定性。比如,连续三个月来自某个客户或平台的收入持续下降,就是一个需要警惕的早期信号。一个简单的、按收入源和月份聚合的图表,比一堆零散的银行通知短信要有用得多。

核心场景三:数据私有与定制化。现成的SaaS服务如QuickBooks、FreshBooks功能强大,但通常价格不菲,且数据存储在第三方。对于收入敏感、技术背景强的独立开发者而言,一个能自己部署、数据完全掌握在自己手中、并且可以根据自己需求稍作修改的工具,吸引力巨大。Earnings Tracker 采用MIT协议,代码完全开源,满足了这部分用户对“控制权”和“成本”(主要是服务器成本)的双重需求。

这个项目就是围绕上述场景设计的。它不试图替代专业的会计实践,而是作为会计流程的前端“数据收集器”,为后续可能的报税、财务分析提供一份干净、统一的源数据。

2. 技术栈选型与架构设计

Indomi/earnings-tracker 的技术栈选择体现了现代全栈JavaScript开发的典型思路:追求开发效率、前后端一致性以及轻量部署。我们深入看一下每个环节的选型逻辑。

2.1 前端:Next.js + Tailwind CSS + Recharts

项目前端基于Next.js 14(采用App Router模式)。选择Next.js而非纯粹的React,主要基于几点考量:

  1. 全栈能力:Next.js允许在同一个项目中无缝编写前端React组件和后端API路由(位于app/api/目录下)。这对于Earnings Tracker这种前后端交互密集、但逻辑相对中度的应用来说,极大地简化了项目结构和部署流程。你不需要单独维护一个后端服务。
  2. 服务端渲染与静态生成:仪表盘页面中的数据(如月度收入图表)非常适合在服务端预先获取和渲染,然后以HTML形式发送给客户端,这能提升首屏加载速度,并对SEO更友好(虽然这个工具主要是自用,但好的性能体验是通用的)。Next.js在这方面提供了开箱即用的支持。
  3. 开发体验:集成的路由、构建、打包等工具链,让开发者可以更专注于业务逻辑。

UI方面,使用了Tailwind CSS。这是一个实用优先的CSS框架。在这样一个个人或小团队维护的项目中,Tailwind能显著提升样式开发效率。你不需要在HTML和CSS文件之间来回切换,也不需要为class命名而烦恼。通过组合工具类,可以快速构建出美观且响应式的界面,这对于需要快速迭代的项目至关重要。

图表库选择了Recharts。这是一个基于React和D3.js构建的图表库。选择它而不是更强大的ECharts或商业化的Highcharts,主要是出于轻量级和与React生态完美集成的考虑。Recharts的声明式API与React组件模型高度契合,学习成本低,且打包体积小。对于Earnings Tracker主要需要的折线图、柱状图、饼图等基础图表,Recharts完全够用,且渲染性能不错。

2.2 后端与数据层:Next.js API Routes + Prisma + SQLite

后端逻辑直接写在Next.js的API Routes中,这是Next.js全栈能力的体现。每个API路由文件(如app/api/earnings/route.ts)对应一个端点,处理相应的HTTP请求(GET, POST等)。

数据访问层使用了Prisma作为ORM。Prisma的优势在于其类型安全的数据库访问。你定义一个schema.prisma数据模型,Prisma Client会自动生成完全类型化的查询构建器。这意味着你在TypeScript代码中调用prisma.earning.findMany()时,编辑器能提供完善的代码补全和类型检查,极大地减少了运行时因字段名拼写错误或类型不匹配导致的Bug。这对于小型项目维护者来说,是一个巨大的效率和安全提升。

数据库选择了SQLite。这是一个非常关键且明智的选择。为什么不用更“标准”的PostgreSQL或MySQL?

  1. 部署简化:SQLite是一个服务器端的数据库,它将整个数据库存储在一个单一的文件中(通常是dev.dbprod.db)。这意味着你不需要额外安装和配置一个数据库服务。在部署时,你只需要确保这个数据库文件有读写权限,并做好备份即可。对于个人使用的工具,这大大降低了运维复杂度。
  2. 资源消耗低:SQLite在内存和CPU占用上都非常轻量,非常适合运行在VPS、甚至是树莓派这类资源有限的环境上。
  3. 足够胜任:Earnings Tracker的数据模型相对简单(主要是收入记录、收入源、用户等几张表),数据量对于个人使用来说也不会太大(每年几千条记录顶天了)。SQLite在并发写入性能上虽有局限,但在这个“个人财务记录”场景下,几乎不存在高并发写入的需求,因此完全够用。

这个“Next.js API + Prisma + SQLite”的组合,构成了一个极其简洁、高效且易于部署的全栈数据流闭环。

2.3 身份认证与安全:NextAuth.js

对于涉及个人财务数据的应用,安全是重中之重。项目采用了NextAuth.js来处理用户认证。NextAuth.js是Next.js生态中事实上的认证标准库,它支持多种认证提供商(如Google、GitHub、Email/Password等)。

在Earnings Tracker中,我推测它主要配置了数据库适配器(使用Prisma),并可能启用了类似GitHub OAuth或简单的邮箱密码登录。使用NextAuth.js的好处是:

  • 开箱即用:它处理了复杂的OAuth流、会话管理、CSRF保护等安全细节。
  • 与Next.js深度集成:可以轻松在API路由和React组件中获取会话状态,例如通过getServerSession来保护API端点,确保只有登录用户才能访问或修改自己的收入数据。
  • 灵活性:可以配置JWT或数据库会话策略,适应不同的部署环境。

注意:在自托管部署时,你必须正确设置NEXTAUTH_SECRET环境变量(一个高强度的随机字符串),这是加密会话令牌的关键。很多部署失败案例都源于此。

3. 核心功能模块拆解与实操

理解了技术栈,我们深入到应用内部,看看它是如何组织代码和实现核心功能的。我会结合项目结构,讲解关键模块的设计。

3.1 数据模型设计:Prisma Schema

一切从数据模型开始。查看项目的prisma/schema.prisma文件,我们可以清晰地看到核心实体及其关系。一个典型的设计可能包含以下主要模型:

model User { id String @id @default(cuid()) email String @unique name String? // ... 其他字段,如image, emailVerified等 (NextAuth相关) accounts Account[] sessions Session[] earnings Earning[] // 一个用户有多条收入记录 incomeSources IncomeSource[] // 一个用户有多个收入源 } model IncomeSource { id String @id @default(cuid()) name String // 收入源名称,如 “GitHub Sponsors”, “Client A” type String // 类型,如 “platform”, “client”, “product” userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) earnings Earning[] // 一个收入源对应多条收入记录 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } model Earning { id String @id @default(cuid()) amount Float // 收入金额 currency String @default(“USD”) // 币种 date DateTime // 收入日期 description String? // 描述(可选) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) incomeSourceId String? incomeSource IncomeSource? @relation(fields: [incomeSourceId], references: [id], onDelete: SetNull) transactionId String? // 外部交易ID,用于去重或关联 createdAt DateTime @default(now()) updatedAt DateTime @updatedAt @@index([userId, date]) // 为按用户和日期查询建立索引,提升性能 }

设计要点解析:

  1. 关系清晰User作为顶层实体,拥有IncomeSource(收入源)和Earning(收入记录)。Earning通过incomeSourceId可选地关联到一个IncomeSource。这种设计允许用户既可以按来源分类收入,也可以记录没有明确来源的收入。
  2. 币种处理Earning模型包含了currency字段。这是一个简化但实用的设计。更复杂的系统可能会引入汇率表和换算逻辑,但作为个人工具,记录原始币种,在展示时按需进行简单换算(或统一为一种基础货币)是更常见的做法。
  3. 索引优化:在Earning上为(userId, date)创建复合索引。因为最常见的查询场景就是“获取某个用户在某个时间段内的收入”。这个索引能大幅加快这类查询的速度。

3.2 API路由设计:RESTful风格与业务逻辑

Next.js的App Router下,API路由位于app/api/目录。Earnings Tracker 的核心API可能包括:

  • GET /api/earnings:获取当前用户的收入记录列表,支持分页、按时间范围过滤、按收入源过滤等查询参数。
  • POST /api/earnings:创建一条新的收入记录。请求体包含amount,date,incomeSourceId,description等。
  • PUT /api/earnings/[id]DELETE /api/earnings/[id]:更新和删除记录。
  • GET /api/income-sources:获取用户的收入源列表。
  • GET /api/analytics/monthly:获取月度收入分析数据,供仪表盘图表使用。

以创建收入记录的API (app/api/earnings/route.ts) 为例,其核心逻辑如下:

import { getServerSession } from “next-auth/next”; import { NextRequest, NextResponse } from “next/server”; import prisma from “@/lib/prisma”; // 初始化好的Prisma Client实例 import { authOptions } from “@/lib/auth”; // NextAuth配置 export async function POST(request: NextRequest) { // 1. 认证检查 const session = await getServerSession(authOptions); if (!session?.user?.id) { return NextResponse.json({ error: “Unauthorized” }, { status: 401 }); } try { const body = await request.json(); const { amount, date, currency, description, incomeSourceId } = body; // 2. 数据验证(简单示例) if (!amount || !date) { return NextResponse.json({ error: “Missing required fields” }, { status: 400 }); } // 3. 关联性检查(如果提供了incomeSourceId,需确保该收入源属于当前用户) if (incomeSourceId) { const validSource = await prisma.incomeSource.findFirst({ where: { id: incomeSourceId, userId: session.user.id }, }); if (!validSource) { return NextResponse.json({ error: “Invalid income source” }, { status: 400 }); } } // 4. 创建记录 const newEarning = await prisma.earning.create({ data: { amount: parseFloat(amount), date: new Date(date), currency: currency || “USD”, description, incomeSourceId: incomeSourceId || null, userId: session.user.id, // 关键:将记录关联到当前登录用户 }, }); // 5. 返回成功响应 return NextResponse.json(newEarning, { status: 201 }); } catch (error) { console.error(“Failed to create earning:”, error); return NextResponse.json({ error: “Internal Server Error” }, { status: 500 }); } }

关键点与避坑指南:

  • 用户隔离:这是多租户SaaS(即使是个人自托管版)的生命线。在每一个数据库查询中,必须显式地加上where: { userId: session.user.id }条件。Prisma的关联查询(如通过include)会自动应用关系约束,但直接使用findUniqueupdate时,务必手动添加用户ID过滤,否则用户A可能看到或修改用户B的数据,造成严重的安全漏洞。
  • 错误处理:对请求体进行基础验证,并给出明确的错误信息。对于数据库操作,使用try-catch包裹,避免服务器错误直接暴露给前端。
  • 类型安全:得益于Prisma和TypeScript,newEarning变量具有完整的类型提示,减少了运行时错误。

3.3 仪表盘与数据可视化实现

仪表盘是用户交互的核心,通常位于根路径 (app/page.tsx)。它会调用分析API获取数据,并使用Recharts渲染。

一个典型的月度收入趋势图组件可能这样实现:

// app/components/monthly-chart.tsx “use client”; // 因为是交互式图表,需要标记为客户端组件 import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from ‘recharts’; interface MonthlyData { month: string; // 格式如 “2024-01” total: number; } export function MonthlyChart({ data }: { data: MonthlyData[] }) { // 格式化月份显示,将“2024-01”转为“Jan 24” const formattedData = data.map(item => ({ ...item, displayMonth: new Date(item.month + ‘-01’).toLocaleDateString(‘en-US’, { month: ‘short’, year: ‘2-digit’ }) })); return ( <div className=“h-[300px] w-full”> <ResponsiveContainer width=“100%” height=“100%”> <LineChart data={formattedData}> <CartesianGrid strokeDasharray=“3 3” stroke=“#eee” /> <XAxis dataKey=“displayMonth” tick={{ fontSize: 12 }} /> <YAxis tickFormatter={(value) => `$${value}`} tick={{ fontSize: 12 }} /> <Tooltip formatter={(value) => [`$${value}`, ‘Earnings’]} labelFormatter={(label) => `Month: ${label}`} /> <Line type=“monotone” dataKey=“total” stroke=“#8884d8” strokeWidth={2} dot={{ r: 4 }} activeDot={{ r: 6 }} /> </LineChart> </ResponsiveContainer> </div> ); }

实操心得:

  • “use client”指令:在Next.js 14的App Router中,默认组件是服务端组件。如果组件使用了React状态(useState)、效果(useEffect)或浏览器API(如window),必须在其顶部添加“use client”;指令。Recharts图表是交互式的,因此必须作为客户端组件。
  • ResponsiveContainer:使用Recharts的ResponsiveContainer包裹图表,可以让图表自适应父容器的宽度和高度,这是实现响应式设计的关键。
  • 数据格式化:将后端API返回的原始数据(如数据库中的日期字符串)在组件内格式化为更友好的显示形式,这是一种关注点分离的好实践。后端负责业务逻辑和原始数据聚合,前端负责展示逻辑。

4. 本地开发与生产部署全流程

4.1 本地开发环境搭建

假设你已经克隆了项目代码,以下是启动本地开发环境的步骤:

  1. 安装依赖

    npm install # 或 yarn install # 或 pnpm install # 推荐,速度更快
  2. 环境变量配置:复制项目根目录下的.env.example文件,重命名为.env.local。这是Next.js读取环境变量的文件。你需要填写关键配置:

    DATABASE_URL=“file:./dev.db” # SQLite数据库文件路径 NEXTAUTH_SECRET=“your-very-long-and-random-secret-key-here” # 生成一个强密钥 NEXTAUTH_URL=“http://localhost:3000” # 本地开发地址 # 如果使用OAuth提供商(如GitHub),还需配置对应的CLIENT_ID和CLIENT_SECRET

    重要提示NEXTAUTH_SECRET必须是一个足够长且随机的字符串。可以在命令行运行openssl rand -base64 32来生成一个。

  3. 初始化数据库:Prisma需要根据schema.prisma生成客户端并创建数据库表。

    npx prisma generate # 生成Prisma Client npx prisma db push # 将数据模型同步到数据库(开发环境) # 或者使用迁移(更适合生产环境) # npx prisma migrate dev --name init
  4. 启动开发服务器

    npm run dev

    访问http://localhost:3000,你应该能看到应用界面。

4.2 生产环境部署指南

对于个人项目,我强烈推荐使用VercelRailway进行部署。它们对Next.js应用有原生的一流支持,并且都提供了简单的数据库(如Neon PostgreSQL或Railway PostgreSQL)和存储方案。但Earnings Tracker使用了SQLite,这是一个需要特别注意的地方。

部署到Vercel(适配SQLite):

Vercel是一个无服务器平台,其文件系统是只读的(除了/tmp)。而SQLite需要写入一个数据库文件。因此,直接部署会导致写入失败。常见的解决方案有:

  1. 使用Vercel的Serverless Functions + 外部存储:这是最推荐的方式。将SQLite数据库文件存储在外部可写的存储服务中,例如:

    • Cloudflare R2:兼容S3 API,免费额度高。
    • Supabase Storage:提供简单的文件存储API。
    • AWS S3:经典选择,但有成本。

    应用启动时,从外部存储下载数据库文件到/tmp目录,然后连接。需要修改数据库连接逻辑,并处理好并发读写问题(SQLite在Serverless环境下并发写入有问题)。这增加了复杂度。

  2. 切换到PostgreSQL(推荐):对于生产环境,尤其是可能有多用户访问的场景,切换到客户端-服务器模式的数据库(如PostgreSQL)是更稳健的选择。Prisma使得这种切换相对平滑。

    • 在Vercel上创建一个PostgreSQL数据库(可以使用Vercel Storage的Postgres,或集成Supabase、Neon等)。
    • .env中的DATABASE_URL替换为PostgreSQL的连接字符串。
    • 运行npx prisma migrate deploy来应用迁移。
    • 修改prisma/schema.prisma中的provider“sqlite”改为“postgresql”,并重新生成Prisma Client。

部署到传统的VPS(如DigitalOcean, Linode):

这是运行SQLite最简单直接的方式。

  1. 准备服务器:购买一台最基础的VPS(如1GB内存),安装Node.js环境(推荐使用nvm)和Git。
  2. 克隆代码并安装依赖
    git clone <your-repo-url> cd earnings-tracker npm install --production
  3. 配置环境变量:在服务器上创建.env.production文件,填入生产环境的配置,特别是DATABASE_URL(指向一个绝对路径,如file:/var/www/earnings-tracker/prod.db)和新的NEXTAUTH_URL(你的域名)。
  4. 构建应用
    npm run build
  5. 使用进程管理器运行:使用PM2来守护进程,确保应用崩溃后自动重启。
    npm install -g pm2 pm2 start npm --name “earnings-tracker” -- start pm2 save pm2 startup # 设置开机自启
  6. 配置反向代理:使用Nginx或Caddy将域名代理到Node.js应用的端口(默认3000)。
  7. 设置SSL证书:使用Let‘s Encrypt的Certbot为你的域名申请免费HTTPS证书。

部署核心心得:对于Earnings Tracker这类个人工具,如果你坚持使用SQLite,那么部署到传统的VPS是最省心、最匹配技术选型的方式。虽然需要一些运维知识,但一旦配置好,运行极其稳定,且成本可控(每月几美元)。如果你想享受Serverless的便利,那么在部署前将数据库切换为PostgreSQL是更明智的选择,这能避免后续很多架构上的麻烦。

5. 扩展思路与个性化定制

开源项目的魅力在于你可以按需修改。以下是一些可能的扩展方向:

5.1 集成更多收入平台API

项目可能已经集成了一些平台,但你可以添加更多。例如,集成Stripe来自动拉取订阅收入。

  1. 在Stripe开发者后台创建Webhook:指向你的应用API端点,例如https://yourdomain.com/api/webhooks/stripe
  2. 创建API路由处理Webhook:在app/api/webhooks/stripe/route.ts中,使用Stripe SDK验证Webhook签名,然后处理事件,如invoice.paid
  3. 解析事件并创建Earning记录:从Stripe事件对象中提取金额、日期、客户信息等,调用内部的prisma.earning.create方法将记录存入数据库。可以将客户邮箱或产品名称映射到本地的IncomeSource

关键点:Webhook处理必须是幂等的(即同一事件处理多次结果相同),因为网络问题可能导致Stripe重发事件。可以在数据库中存储已处理事件的Stripe事件ID,创建记录前先检查是否已存在。

5.2 添加汇率转换与多币种支持

基础版本可能只存储原始币种。你可以增强它:

  1. 创建汇率表:在Prisma Schema中新增一个ExchangeRate模型,存储基准货币(如USD)和目标货币的汇率及日期。
  2. 定时任务获取汇率:使用Node的定时任务库(如node-cron)或利用Serverless Function的定时触发器,每天从公开API(如exchangerate-api.com)获取最新汇率并存入数据库。
  3. 在查询时换算:在获取收入列表或统计的API中,根据用户设置的“显示货币”偏好,将每条记录的amount按收入发生日期的汇率换算后,再求和或展示。

5.3 增强数据分析与报表

除了月度趋势,可以添加:

  • 收入源构成分析:饼图展示过去一年各收入源的占比。
  • 预测功能:基于历史收入数据,使用简单的移动平均或线性回归,预测未来几个月的收入趋势。
  • 导出功能:将选定时间段的收入数据导出为CSV或PDF,方便报税或存档。

5.4 改进UI/UX

  • 数据表格:使用类似TanStack Table的库,为收入列表添加排序、过滤、分页功能。
  • 快捷录入:在仪表盘增加一个浮动按钮,点击后弹出快速表单,只需输入金额和选择来源,日期默认为今天,简化录入流程。
  • 移动端优化:确保Tailwind CSS的响应式设计在手机端有良好体验,考虑开发PWA,使其可以安装到手机主屏幕。

6. 常见问题与故障排查

在部署和使用过程中,你可能会遇到以下问题:

6.1 数据库连接问题

问题:应用启动失败,报错PrismaClientInitializationError,提示无法打开数据库文件。排查

  1. 文件路径与权限:检查DATABASE_URL中的文件路径是否正确,以及运行Node.js进程的用户(如www-data或你的用户名)是否有对该路径的读写权限。在Linux上,可以使用ls -l /path/to/your.db查看权限,并用chownchmod命令修改。
  2. 目录是否存在:确保数据库文件所在的目录已经存在。Prisma不会自动创建不存在的目录。
  3. 生产环境与开发环境混淆:确保你正确设置了环境变量NODE_ENV=production,并且加载的是.env.production文件,而不是.env.local

6.2 NextAuth 认证失败

问题:点击登录后跳转回原页面,或者提示“Callback URL错误”。排查

  1. NEXTAUTH_URL:这是最常见的错误原因。确保NEXTAUTH_URL环境变量与你实际访问应用的地址完全一致,包括http://https://。在Vercel等平台上,有时需要设置为https://your-app.vercel.app
  2. OAuth提供商配置:如果使用GitHub或Google登录,请确保在对应的开发者后台正确配置了回调URL(Callback URL),格式通常为{NEXTAUTH_URL}/api/auth/callback/{provider}
  3. Secret密钥:确保NEXTAUTH_SECRET已设置且足够强。在生产环境中,它必须被定义。

6.3 图表不显示或数据为空

问题:仪表盘页面正常加载,但图表区域空白或显示“无数据”。排查

  1. 检查API请求:打开浏览器开发者工具的“网络”(Network)标签页,查看图表组件发起的API请求(如/api/analytics/monthly)是否成功(状态码200),以及返回的JSON数据结构是否符合预期。
  2. 检查组件数据流:确认父组件是否正确获取了数据,并通过props传递给了图表子组件。可以在图表组件内添加console.log(data)来调试。
  3. Recharts客户端渲染:确认图表组件顶部有“use client”;指令。没有它,在服务端组件中Recharts无法正确渲染。
  4. 日期格式:确保传递给图表的数据中,用于X轴的日期字段是字符串格式,且能被Recharts正确解析。如果后端返回的是Date对象,可能需要先序列化。

6.4 性能问题:页面加载缓慢

问题:仪表盘页面,特别是包含大量历史数据时,加载很慢。优化

  1. 数据库索引:确认已在Earning表的userIddate字段上建立了复合索引。使用npx prisma studio或直接SQL命令检查。
  2. API优化:在月度分析API中,确保Prisma查询使用了where条件限制当前用户,并且只查询必要的字段(使用select而非默认的include all)。对于聚合查询,确保在数据库层面完成计算(使用Prisma的_sum,_avg等聚合函数),而不是取回所有数据在JavaScript中计算。
  3. 前端数据缓存:考虑使用React Query或SWR库来缓存API响应。这样,用户在切换页面再返回仪表盘时,可以立即看到缓存的数据,同时在后端静默获取更新。
  4. 分页与懒加载:对于收入记录列表,实现分页查询,避免一次性加载成千上万条记录。

这个项目是一个非常好的起点,它展示了一个完整、现代的全栈应用应该如何构建。通过理解它的每一层,你不仅能把它用起来,更能掌握定制和扩展它的能力,让它真正成为贴合你个人工作流的得力助手。

http://www.jsqmd.com/news/817247/

相关文章:

  • 【NotebookLM视频转文字黄金标准】:基于237小时教育/会议/访谈视频测试的ASR校准模型与人工后编译SOP
  • 别再只调白平衡了!手把手教你用CCM矩阵精准校正相机色彩(附24色卡RGB参考)
  • Babel Handbook终极指南:掌握JavaScript转译的完整教程 [特殊字符]
  • nDreamBerd代码片段管理:复用优质代码的终极指南
  • 别再纠结CCD和CMOS了!工业相机选型,这4个实战场景帮你一次搞懂
  • NotebookLM动态图表总崩溃?谷歌内部文档未公开的4种兼容性修复方案
  • 程序员VS项目经理:为什么负责“写代码”的反而挣得少?
  • 别再手动调增益了!手把手教你用RFSoC的AGC功能搞定动态信号(附Vivado 2023.1工程配置)
  • Unix 命令 mkdir 详细介绍
  • 细长手腕支架加工:两端φ11轴孔相距130mm,同轴差一点手腕就偏转 - 莱图加精密零件加工
  • 脑机接口实战:用SSVEPNet搞定短时脑电信号分类,附完整代码与数据集
  • Windows本地开发,如何用Zookeeper 3.6.2为你的Spring Cloud微服务搭建注册中心?
  • SuperMap GIS 三维性能跃迁:从硬件选型到显卡驱动的深度调优指南
  • 企业微信打卡数据拉取太慢?我用SQL Server存储过程优化了15秒加载到3秒
  • 小白必看!OpenClaw 完整版汉化配置实操步骤
  • 陷门矩阵技术:高效安全的云端线性代数计算方案
  • 芯片老化板制作,尺寸接口与工位数量的秘密
  • 如何找到靠谱的PMP培训?5个标准筛掉90%的不合格机构
  • Midjourney Pro订阅后必须立即配置的4项安全策略(含会话隔离等级、生成日志留存周期与团队权限熔断机制)
  • Nginx Server Configs负载均衡配置:分布式系统优化的终极指南
  • 告别AI失忆:用Agentic Code框架打造稳定高效的AI编程协作
  • poi-tl循环表格踩坑实录:从EasyExcel读取到Word渲染,完整避坑指南
  • 告别默认主题!手把手教你配置5款高颜值oh-my-zsh主题(附效果图与一键切换命令)
  • 【零基础部署】Ollama 部署 Qwen2.5 保姆级教程
  • MonoGame UI动画系统:掌握过渡效果与插值函数的终极指南
  • LServe长序列LLM服务系统:混合稀疏注意力优化实践
  • 地缘政治市场模拟器:从事件向量化到多资产联合模拟的工程实践
  • 青少年祛痘精华哪家好:蜜妙诗行业龙头 - 17322238651
  • Profound走红背后:GEO服务商如何突破技术与市场双重挑战?
  • 终极Git分支策略指南:企业团队高效协作的7个核心方法