
1. 项目概述一个按需付费的文本AI API如果你正在开发一个需要文本处理功能比如摘要、关键词提取、翻译的独立项目、小工具或者只是偶尔需要批量处理一些文档你大概率遇到过这样的困境市面上主流的AI服务API几乎都要求你绑定信用卡并选择一个固定的月度套餐。哪怕你这个月只用了一次也得为整个套餐付费。这种模式对于需求不稳定、预算有限的开发者或小团队来说非常不友好大量的额度就在等待中被浪费了。我最近就为了解决这个问题自己动手搭建了一个完全不同的服务一个基于Solana区块链和USDC稳定币的、真正按使用次数付费的文本AI API。它的核心逻辑很简单——用一次付一次的钱没有月费没有订阅更没有最低消费。你只需要为实际消耗的计算资源买单。整个服务的后端跑在Deno Deploy上支付环节则利用了Solana网络近乎为零的交易手续费和USDC的全球流通性绕开了传统金融体系对信用卡、地域和退款纠纷的限制。这个项目的关键词是payperuse按使用付费、text文本、ai人工智能和api接口。它非常适合那些希望将AI能力低成本、灵活地集成到自己产品中的开发者尤其是面向全球用户、不希望处理复杂支付流程的独立开发者。接下来我会详细拆解这个项目的设计思路、技术实现细节并分享在开发过程中踩过的坑和总结的经验。2. 核心设计思路与架构选型2.1 为什么选择“按使用付费”模式传统的SaaS订阅模式有其优势比如收入可预测、用户粘性高。但对于工具类API特别是面向开发者的API订阅制带来了几个明显的痛点使用量不匹配开发者项目有淡旺季或者项目本身处于原型验证阶段使用频率极低。为可能用不到的额度预先付费增加了试错成本和现金流压力。门槛过高对于学生、业余项目爱好者或初创公司动辄数十美元的月费是一笔不小的开支可能直接让他们放弃使用。资源浪费从服务提供商角度看用户预购的额度如果未被使用本质上也是一种资源错配。而按需付费能更精确地反映实际资源消耗。因此按使用付费Pay-per-use模型更像是一种“效用计算”Utility Computing如同我们为水电付费一样用多少付多少。这种模式尤其适合偶发性工作负载例如周末运行的数据清洗脚本、为博客文章批量生成摘要、为用户上传的文档即时提取关键词等场景。它降低了用户的使用门槛也让服务的定价更加公平透明。2.2 为什么选择Solana和USDC作为支付方案确定了按需付费的模式后下一个难题就是支付系统。传统的支付网关Stripe, PayPal等虽然成熟但存在几个问题地域限制很多支付服务对特定国家/地区的用户支持不友好。信用卡依赖与拒付风险需要用户拥有信用卡并且作为商家需要面对“拒付”风险这对于小额、高频的微支付场景是致命的。手续费高昂对于单笔可能只有几美分甚至更低的API调用费用传统支付网络的手续费占比会高得离谱使得微支付在经济上不可行。集成复杂度需要处理KYC了解你的客户、合规、账单等一系列复杂问题。区块链和加密货币特别是稳定币为这个问题提供了一个优雅的解决方案。我选择了Solana网络和其上的USDC稳定币原因如下极低的交易成本Solana网络的平均交易费用在0.00025美元左右这意味着即使是一笔1美元的支付手续费占比也微乎其微完美支持微支付。快速的交易确认Solana出块时间约400毫秒交易通常在几秒内完成最终确认用户体验接近即时支付。USDC的稳定性USDC是一种与美元1:1锚定的稳定币价格波动极小。用户支付和开发者收入的价值是稳定的避免了加密货币价格剧烈波动带来的计价混乱。全球性与无许可性任何人在任何地方只要有一个Solana钱包如Phantom, Solflare就可以进行支付没有地域限制无需银行账户。可编程性与自动化区块链交易是透明且可验证的。通过监听链上事件可以自动化地完成“支付到账-API额度发放”的整个流程无需人工干预。这套组合拳使得构建一个全球可访问、低成本、抗审查的微支付API服务成为可能。架构上我采用了无服务器Serverless的思路将API服务部署在Deno Deploy上它提供了快速的全球边缘网络、简单的部署流程和与Deno运行时原生的TS/JS支持非常适合快速构建和迭代此类轻量级API。3. 系统架构与核心组件详解整个系统可以清晰地分为三个层次前端API层、业务逻辑层和区块链交互层。它们协同工作实现从API调用到链上支付确认的完整闭环。3.1 API服务层Deno Deploy我选择Deno和Deno Deploy作为运行时和部署平台主要基于以下几点考量开发效率Deno内置了TypeScript支持、安全权限管理和标准库无需复杂的node_modules和构建步骤。一个文件就能启动一个HTTP服务器非常适合快速原型开发。部署简单Deno Deploy与GitHub集成git push即可完成全球部署。它本身就是一个边缘计算平台能保证API的低延迟响应。成本可控对于初期流量不大的服务Deno Deploy的免费额度足够使用后续按请求和计算时间付费的模式也与我们的“按使用付费”理念契合。API层主要提供以下几个端点POST /keys/create: 创建新用户并生成一个唯一的API Key同时赠送初始的演示额度。POST /summarize,/keywords,/translate: 核心的文本处理功能端点。POST /credits/buy: 处理用户购买额度的请求生成一个一次性的Solana存款地址。GET /credits/balance: 查询用户当前剩余额度。每个请求都需要在Header中携带Authorization: Bearer YOUR_API_KEY。服务端会首先验证API Key的有效性并检查用户余额是否足以支付本次调用。如果余额不足立即返回402 Payment Required状态码。3.2 业务逻辑与信用系统这是系统的“大脑”负责管理用户、额度和计费规则。用户与API Key管理每个新用户通过/keys/create端点注册系统会生成一个唯一的UUID作为用户ID并同时生成一个随机的、高熵的字符串作为API Key。这个映射关系被持久化存储。我使用了Deno KVDeno内置的键值存储来存储这些数据因为它与Deno Deploy集成良好且能满足初期数据存储需求。// 示例存储用户信息 const userKey [users, userId]; await kv.set(userKey, { apiKey: generatedApiKey, creditBalance: 100, // 初始赠送100演示点数 createdAt: new Date(), });信用点数系统为了简化支付逻辑系统内部使用“信用点数”作为计量单位。定价策略如下1 USDC 1000 信用点数。摘要Summarize每次调用消耗10点。关键词提取Keywords每次调用消耗5点。翻译Translate每次调用消耗15点。这样设计的好处是用户可以用USDC购买整数单位的点数包如1 USDC买1000点而内部计费可以更精细比如5点/次。当用户调用API时业务逻辑层会先进行点数扣减然后再执行AI处理任务。这里有一个关键的设计扣减操作必须是原子的并且要先于服务执行以防止用户余额不足却消耗了计算资源。AI服务集成文本处理的核心能力依赖于第三方AI模型。为了保持灵活性和成本我选择了按调用付费的AI服务提供商例如OpenAI的GPT API、Google的Vertex AI或是开源的模型API。在当前的实现中摘要和关键词提取可以调用GPT-3.5-turbo这类模型通过设计合适的Prompt来实现翻译则可以使用专门的翻译API如DeepL或大模型的翻译能力。这一部分被抽象成独立的服务模块方便未来切换或升级模型。3.3 区块链支付与监听层这是最具创新性也最需要谨慎处理的部分。目标是实现用户支付USDC到指定地址 - 系统检测到支付 - 自动为用户充值信用点数。生成一次性存款地址当用户通过/credits/buy端点发起购买请求时例如传入{usdc_amount: 1}后端不会让用户支付到某个固定的公司钱包。那样会面临巨大的对账难题。相反系统会为每一笔交易生成一个唯一的、一次性的Solana地址。这个地址实际上是一个程序派生地址由服务端的某个主钱包公钥和一个唯一的“订单ID”通过findProgramDerivedAddress函数派生而来。这个地址只接收特定金额1 USDC的转账并且关联了用户的ID和订单状态。支付监听与确认系统需要一个“监听者”来持续扫描Solana区块链检查那些一次性存款地址是否收到了正确金额的USDC。这里有两种实现方式WebSocket订阅连接到Solana的RPC节点如Helius, QuickNode订阅相关程序账户的变更。一旦目标地址的USDC余额发生变化就会收到通知。定时轮询启动一个后台任务Cron Job定期例如每30秒通过RPC查询所有待处理订单的存款地址余额。我采用了第二种方式因为它实现起来更简单对RPC服务的压力也更可控尤其适合初期阶段。这个后台任务运行在另一个独立的Deno Deploy Cron Job中。交易验证与点数发放当监听器发现某个地址收到了足额的USDC它不能立即充值。必须进行严格的验证验证交易通过交易签名Tx Signature获取完整的交易详情确认转账方、接收方、转账金额和代币类型必须是USDC。验证金额确认到账金额与用户请求的金额一致防止用户多付或少付造成混乱。防止重放攻击确保该笔交易或该订单没有被处理过。这通过更新订单状态为“已确认”来实现。 只有所有验证通过后系统才会执行kv.set操作将对应的信用点数加到用户的账户余额上。注意安全是重中之重。处理区块链交易时私钥的管理必须绝对安全。服务端的主钱包私钥应通过环境变量注入并且该钱包只用于生成派生地址和支付Gas费如果需要绝不用于存储用户资金。用户资金直接发送到派生地址这些地址的私钥理论上只有系统知道通过派生算法且资金应定期归集到更安全的冷钱包中。4. 实操部署与核心代码解析让我们深入到具体实现看看关键部分代码是如何工作的。假设你已经安装了Deno并且有一个Deno Deploy账户。4.1 项目初始化与依赖首先创建一个项目目录并初始化主要的服务器文件main.ts。// main.ts import { Application, Router } from https://deno.land/x/oakv12.6.1/mod.ts; import { create, verify } from https://deno.land/x/djwtv2.9.1/mod.ts; // 用于JWT这里我们简化直接用API Key // 初始化路由和App const router new Router(); const app new Application(); // 中间件日志、错误处理等 app.use(async (ctx, next) { console.log(${ctx.request.method} ${ctx.request.url.pathname}); await next(); }); // 在这里定义路由... router.post(/keys/create, createKeyHandler); router.post(/summarize, authMiddleware, summarizeHandler); // ... 其他路由 app.use(router.routes()); app.use(router.allowedMethods()); await app.listen({ port: 8000 });我们使用oak框架处理HTTP路由。用户认证采用最简单的API Key模式在内存或KV中验证即可无需复杂的JWT。4.2 实现API Key创建与认证中间件/keys/create端点负责创建新用户。为了吸引用户尝试我们赠送100点演示额度。import { crypto } from https://deno.land/std0.203.0/crypto/mod.ts; async function createKeyHandler(ctx: any) { const userId crypto.randomUUID(); // 生成一个安全的随机字符串作为API Key const apiKeyBuf new Uint8Array(32); crypto.getRandomValues(apiKeyBuf); const apiKey Array.from(apiKeyBuf, (byte) byte.toString(16).padStart(2, 0)).join(); // 存储到Deno KV const kv await Deno.openKv(); await kv.set([users, userId], { apiKey, creditBalance: 100, // 初始赠送100点 createdAt: new Date(), }); // 注意实际存储时应该对apiKey进行哈希存储验证时对比哈希值。这里为演示简化。 await kv.set([api_keys, apiKey], userId); // 建立API Key到用户ID的快速索引 ctx.response.body { apiKey, credits: 100, userId }; ctx.response.status 201; }认证中间件会在每个需要鉴权的请求前执行检查API Key并获取用户信息。async function authMiddleware(ctx: any, next: any) { const authHeader ctx.request.headers.get(Authorization); if (!authHeader || !authHeader.startsWith(Bearer )) { ctx.response.status 401; ctx.response.body { error: Missing or invalid Authorization header }; return; } const apiKey authHeader.substring(7); const kv await Deno.openKv(); const userId await kv.get([api_keys, apiKey]); if (!userId || !userId.value) { ctx.response.status 401; ctx.response.body { error: Invalid API Key }; return; } // 将用户ID和余额信息挂载到ctx.state供后续处理器使用 const userInfo await kv.get([users, userId.value]); if (!userInfo.value) { ctx.response.status 500; ctx.response.body { error: User data corrupted }; return; } ctx.state.user userInfo.value; ctx.state.userId userId.value; await next(); }4.3 实现文本处理端点以摘要为例在summarizeHandler中我们需要先扣费再调用AI服务。async function summarizeHandler(ctx: any) { const user ctx.state.user; const cost 10; // 摘要服务消耗10点 if (user.creditBalance cost) { ctx.response.status 402; // Payment Required ctx.response.body { error: Insufficient credits. Please top up. }; return; } // 1. 原子性扣费 const kv await Deno.openKv(); const userKey [users, ctx.state.userId]; // 使用KV的原子操作确保并发安全 const result await kv.atomic() .check(userInfo) // 确保数据未被其他人修改 .set(userKey, { ...user, creditBalance: user.creditBalance - cost }) .commit(); if (!result.ok) { ctx.response.status 409; // Conflict ctx.response.body { error: Balance update conflict, please retry. }; return; } // 2. 获取请求正文 const body await ctx.request.body().value; const { text, max_sentences 3 } body; if (!text) { ctx.response.status 400; ctx.response.body { error: Missing text field }; return; } // 3. 调用AI服务示例调用OpenAI API const openAiApiKey Deno.env.get(OPENAI_API_KEY); const response await fetch(https://api.openai.com/v1/chat/completions, { method: POST, headers: { Authorization: Bearer ${openAiApiKey}, Content-Type: application/json, }, body: JSON.stringify({ model: gpt-3.5-turbo, messages: [ { role: system, content: You are a helpful assistant that summarizes text concisely. }, { role: user, content: Summarize the following text in ${max_sentences} sentences or less:\n\n${text} } ], max_tokens: 150, temperature: 0.5, }), }); if (!response.ok) { // 如果AI调用失败可以考虑回滚点数这是一个设计决策。这里我们先记录日志返回错误。 console.error(OpenAI API call failed:, await response.text()); ctx.response.status 502; // Bad Gateway ctx.response.body { error: AI service temporarily unavailable }; // 注意点数已被扣除可能需要一个补偿或重试机制。生产环境需要更完善的错误处理。 return; } const data await response.json(); const summary data.choices[0]?.message?.content?.trim(); ctx.response.body { summary }; }4.4 实现USDC购买端点这是与区块链交互的核心。我们需要solana/web3.js库。import { Connection, Keypair, PublicKey, Transaction, SystemProgram, LAMPORTS_PER_SOL } from https://esm.sh/solana/web3.js; import { createAssociatedTokenAccountInstruction, getAssociatedTokenAddress, createTransferInstruction, getAccount } from https://esm.sh/solana/spl-token; const SOLANA_RPC_URL Deno.env.get(SOLANA_RPC_URL) || https://api.mainnet-beta.solana.com; const connection new Connection(SOLANA_RPC_URL); // 服务端主钱包用于派生地址和支付可能的创建账户费用 const serverWallet Keypair.fromSecretKey( Uint8Array.from(JSON.parse(Deno.env.get(SERVER_WALLET_PRIVATE_KEY)!)) ); const USDC_MINT new PublicKey(EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v); // 主网USDC代币地址 async function buyCreditsHandler(ctx: any) { const { usdc_amount } await ctx.request.body().value; const userId ctx.state.userId; if (!usdc_amount || usdc_amount 0) { ctx.response.status 400; ctx.response.body { error: Invalid usdc_amount }; return; } const orderId crypto.randomUUID(); // 为这个订单生成一个唯一的派生地址PDA // 这里简化实际上我们需要一个程序ID来派生。更简单的做法是生成一个新的密钥对。 // 但为了安全和管理方便生产环境应使用PDA。 // 本例中我们模拟生成一个新地址。 const tempKeypair Keypair.generate(); const depositAddress tempKeypair.publicKey; // 计算用户应获得的点数 const creditsToAdd usdc_amount * 1000; // 将订单信息存入KV状态为pending const kv await Deno.openKv(); await kv.set([orders, orderId], { userId, depositAddress: depositAddress.toBase58(), usdcAmount: usdc_amount, creditsToAdd, status: pending, createdAt: new Date(), // !!! 重要存储临时密钥对的私钥仅用于演示生产环境绝对不要这样做 // 生产环境应使用PDA或使用可追踪的存款地址方案。 }); // 返回存款地址给用户 ctx.response.body { orderId, depositAddress: depositAddress.toBase58(), requiredAmount: usdc_amount, credits: creditsToAdd, memo: Payment for order ${orderId}. Send exact USDC amount to this address., instructions: Send exactly ${usdc_amount} USDC to the address above on the Solana network. Then, confirm your payment by calling the /credits/confirm endpoint with your transaction signature. }; }重要警告上述代码中为每个订单生成全新密钥对并存储私钥是极其危险的做法仅用于演示概念。生产环境中你必须使用程序派生地址或者使用一个更安全的托管服务来处理临时存款地址的生成和监控确保私钥永不暴露在应用代码或数据库中。4.5 部署到Deno Deploy将代码推送到GitHub仓库。在Deno Deploy控制台链接你的GitHub账户导入项目仓库。在项目设置中配置环境变量OPENAI_API_KEY,SOLANA_RPC_URL,SERVER_WALLET_PRIVATE_KEY生产环境务必使用密钥管理服务如Deno Deploy的Secret功能。部署。Deno Deploy会自动构建并发布你的应用。对于支付监听的后台任务你可以在Deno Deploy中创建一个Cron Job。新建一个文件例如cron_listener.ts里面包含一个定时执行的函数用于查询所有pending状态的订单存款地址余额并更新状态。5. 安全、成本与扩展性考量构建这样一个系统在兴奋之余必须冷静地面对一系列现实挑战。5.1 安全陷阱与防范措施私钥管理这是区块链应用的头号杀手。绝对不要将私钥硬编码在代码中或提交到版本库。使用环境变量或专业的密钥管理服务如AWS Secrets Manager, HashiCorp Vault。服务端钱包的私钥权限应被严格控制最好使用多签钱包或硬件钱包管理大量资金。重放攻击与双重支付必须确保每一笔链上交易只被处理一次。通过维护一个“已处理交易签名”的集合并在验证时严格检查可以防止重放。订单状态pending/confirmed/failed的原子性更新也至关重要。API滥用与DDoS按次付费并不能完全防止滥用。恶意用户可能通过大量小额支付发起攻击。需要实施速率限制Rate Limiting基于IP或API Key。对于新用户可以设置较低的初始免费额度或首次购买门槛。前端安全如果你提供Web前端让用户连接钱包并支付务必使用官方钱包适配器并验证交易详情防止网络钓鱼或交易篡改。数据一致性扣费KV操作和AI服务调用之间可能存在不一致。如果扣费成功但AI调用失败用户点数被扣但未获得服务。可以考虑引入“事务补偿”机制或者在AI调用失败后尝试返还点数并记录日志以便人工核查。5.2 成本模型与盈利分析你的成本AI服务成本这是大头。以OpenAI GPT-3.5-turbo为例每1000个token输入约$0.0005输出约$0.0015。一次摘要调用可能消耗1000输入200输出token成本约$0.0007。我们定价10点/$0.01意味着一次摘要收入$0.0001。这里存在严重的价格倒挂。你需要要么使用成本更低的模型如开源模型自托管但增加运维成本。提高定价或调整点数消耗比例。处理更长的文本让单次调用价值更高。区块链交易成本Solana交易费极低~$0.00025但如果你需要为用户创建的临时地址初始化即创建关联代币账户可能需要支付约0.002 SOL的租金豁免费约$0.02。这笔费用需要由你承担或转嫁给用户。服务器成本Deno Deploy等Serverless平台按请求和计算时间收费。需要精细测算API调用和Cron Job的成本。定价策略1 USDC 1000点的定价需要仔细核算。必须确保单次调用的收入 (AI成本 区块链成本/摊销 服务器成本/摊销 利润)。对于微支付规模效应非常重要。5.3 扩展性与优化方向支持更多区块链和代币可以扩展支持Polygon、Avalanche等其他低费用链上的USDC甚至支持原生代币支付以吸引更广泛的加密用户。引入预签名交易为了更好的用户体验可以让用户在前端签名一笔已经构建好的交易然后由后端直接发送用户无需手动复制地址和金额减少操作失误。实现即时充值通知使用Solana的WebSocket订阅替代轮询可以在用户支付后几秒内即时到账体验更佳。提供套餐包虽然主打按次付费但可以提供“批量折扣”例如支付10 USDC获得11000点激励用户充值更多。功能扩展除了摘要、关键词、翻译可以增加情感分析、文本分类、内容改写、语法检查等多种NLP功能。开发者体验提供详细的API文档、多种语言的SDKPython, Node.js, Go等、Webhook通知当余额不足时等。6. 常见问题与故障排查实录在实际开发和运营中我遇到了不少问题这里记录下最典型的几个及其解决方案。6.1 支付相关问题问题1用户支付了USDC但点数迟迟未到账。可能原因监听器延迟或故障后台Cron Job可能执行失败或延迟。RPC节点问题使用的公共RPC节点不稳定或响应慢。交易验证失败用户支付的金额不精确或者支付到了错误的代币不是USDC。订单状态冲突并发处理导致订单状态更新异常。排查步骤检查Cron Job的运行日志确认任务是否正常执行。在Solana区块链浏览器如Solscan上手动输入用户提供的存款地址和交易签名确认交易是否成功、金额是否正确、代币是否为USDC。检查数据库Deno KV中该订单的状态。如果是pending且交易已验证可能是点数添加逻辑出错。如果是confirmed则检查用户余额更新日志。考虑实现一个手动补单的运维接口用于处理极端情况。问题2用户尝试支付但钱包显示“创建关联代币账户”需要支付SOL租金。原因与解决如果用户的USDC接收地址我们的临时存款地址之前从未接收过USDC那么在首次接收时需要在链上创建该代币的关联账户这需要支付一小笔SOL作为租金豁免。有两种处理方式让用户支付体验差用户需要同时拥有SOL和USDC。服务端预创建推荐在生成存款地址后服务端主动发起一笔交易用主钱包的SOL为该地址创建好USDC关联账户。这笔成本约0.002 SOL需要计入你的运营成本。代码上可以在buyCreditsHandler中生成地址后立即检查并创建账户。6.2 API服务相关问题问题3AI服务调用超时或返回错误导致用户点数被扣但无结果。解决这是分布式系统典型的“扣款成功服务失败”问题。我的策略是快速失败与重试在调用AI API时设置合理的超时如10秒。如果超时或返回5xx错误立即向用户返回一个明确的错误如503 Service Unavailable并不执行扣费。这要求扣费逻辑在确认AI服务可用后才执行或者能安全地回滚。异步处理与补偿更复杂的方案是将请求放入队列。先扣费然后异步处理AI任务处理完成后通过Webhook或让用户轮询结果。如果异步处理失败则有一个补偿Job负责返还点数并通知用户。实施熔断器如果某个AI服务提供商持续故障自动熔断对其的调用并切换到备用服务或直接返回错误避免资源浪费。问题4遭遇恶意刷取免费演示额度。解决100点的免费额度本意是吸引真实用户。但有人会写脚本批量创建API Key。增加获取门槛要求验证邮箱或通过GitHub账号登录后才发放。行为分析对来自同一IP或类似模式的创建请求进行速率限制。降低额度将初始额度降至10或20点仅够体验1-2次调用降低被刷的损失。6.3 性能与运维问题问题5Deno KV的读写延迟在高并发下成为瓶颈。解决Deno KV是最终一致性的数据库。对于余额检查这种需要强一致性的操作可能会遇到问题。使用原子操作如示例代码所示使用.atomic().check().set().commit()来确保余额检查与扣减的原子性。引入缓存对于用户余额等读多写少的数据可以在内存或Redis中缓存定期与KV同步。但要注意缓存一致性问题。评估数据库如果流量增长需要考虑迁移到更强大的数据库如PostgreSQL。问题6Solana RPC节点限流或不可用。解决公共RPC节点有速率限制。对于需要频繁查询链上数据的支付监听服务这不可接受。使用付费RPC服务订阅像Helius、QuickNode、Triton这样的专业RPC服务商它们提供更高的速率限制、更可靠的连接和专属节点。自建节点对于极高流量的应用可以考虑自建Solana验证器或RPC节点但这需要很高的技术和资金投入。这个项目将区块链微支付与云函数API相结合为开发者提供了一种全新的、灵活的AI服务消费模式。它验证了在Solana这样的高性能区块链上构建实用型DApp的可行性。虽然过程中充满了在安全、成本和系统设计上的挑战但看到用户能够真正地“用一次付一次”并且来自世界任何角落这种体验是传统支付方式难以提供的。如果你也想尝试类似的项目我的建议是从小处着手先实现核心的“支付-服务”闭环并投入大量精力在安全设计和成本核算上然后再考虑扩展功能和优化体验。