从需求到上线:记录一次完全由 AI 辅助完成的小产品全流程

从需求到上线:记录一次完全由 AI 辅助完成的小产品全流程

上周产品突然甩过来一个需求:给销售团队做一个「客户报价计算器」,要能选产品型号、填数量、自动算折扣、导出 PDF,还要在手机上能用。需求文档就三句话,排期却只有两天。

放在以前,这种需求我得先画原型、再写 PRD、切图、写前端、调接口、写后端、配部署,两天连水都顾不上喝。但这一次,我尝试让 AI 深度参与整个流程,从需求理解到代码生成再到上线部署,最终 6 个小时搞定。这篇文章不是吹 AI 多厉害,而是把这次实战中踩过的坑、值得复用的流程、以及我觉得「人不能偷懒」的环节,完整记录下来。

一、需求拆解:先把"人话"变成"结构化输入"

AI 写代码再强,也怕需求模糊。我拿到需求后,没有直接把那三句话丢给 AI,而是先用 10 分钟做了一个最小化的需求清单:

  • 核心功能:选择产品型号 → 输入数量 → 自动计算总价 → 应用折扣 → 导出 PDF 报价单
  • 用户角色:销售(移动端使用)、管理员(后台维护型号和价格)
  • 技术约束:两天上线、无后端预算、要导出 PDF、支持微信内打开
  • 数据:5 个产品型号,每个型号有基础价、阶梯折扣、有效期

我把这个清单整理成 Markdown 表格,然后让 AI 帮我生成一份更正式的 PRD。这一步的关键不是让 AI 替代思考,而是让 AI 把模糊需求里的隐藏问题暴露出来。比如 AI 在反问我时提了三个问题:

  1. 折扣是固定比例还是按数量阶梯?
  2. PDF 是客户端生成还是服务端生成?
  3. 管理员修改价格是否需要登录鉴权?

这三个问题如果不提前想清楚,后面一定会返工。所以第一阶段的经验是:人可以偷懒不写文档,但不能偷懒不思考。AI 适合把思考结果结构化,而不是替你做判断。

二、技术方案:用"最小可用架构"砍掉了 80% 的复杂度

确定需求后,我开始和 AI 一起定方案。最初 AI 给了一个很"正规"的架构:Next.js + Prisma + PostgreSQL + Docker + Nginx。这个方案没问题,但两天上线不可能。

我给了它一个约束:「无后端、无数据库、纯前端部署到静态托管」。AI 立刻换了一套更轻量的方案:

  • 前端:Vue 3 + Vite + Tailwind CSS,移动端优先
  • 数据:产品型号和价格写死在一个 JSON 文件里,管理员直接改 Git
  • PDF 导出 :客户端用 html2canvas + jspdf 生成
  • 部署:Vercel 自动部署,Git 提交即上线
  • 版本管理:GitHub 仓库 + 分支保护

这个方案看起来不"企业级",但完全符合"两天上线"的约束。更重要的是,它没有引入不必要的复杂度。销售团队要的是一个能用的报价工具,不是一个需要运维的微服务架构。

代码层面,我把产品数据独立成一个 products.json

javascript 复制代码
// src/data/products.json
[
  {
    "id": "ess-100k",
    "name": "工商业储能柜 100kWh",
    "basePrice": 128000,
    "unit": "台",
    "tiers": [
      { "min": 1, "max": 5, "discount": 0.98 },
      { "min": 6, "max": 20, "discount": 0.92 },
      { "min": 21, "discount": 0.85 }
    ]
  }
]

这样的好处是,当销售总监第二天说要调整价格时,我只需要改这个 JSON 文件再 push,2 分钟后线上就生效了。

三、AI 辅助编码:我负责决策,AI 负责实现细节

进入编码阶段后,我把整个项目拆成 6 个任务:

  1. 项目初始化与路由
  2. 产品选择与数量输入组件
  3. 价格计算引擎
  4. PDF 报价单渲染
  5. 移动端样式适配
  6. Vercel 部署配置

每个任务我都会先写一个简短的需求描述,然后让 AI 生成代码。但这里有一个关键操作:我不会直接复制粘贴 AI 的输出,而是让它先解释代码的关键逻辑,我再决定要不要用。

比如价格计算引擎,AI 第一次给出的实现是这样的:

javascript 复制代码
// src/utils/priceEngine.js
export function calculateQuote(items) {
  return items.reduce((total, item) => {
    const product = getProductById(item.productId);
    const tier = product.tiers
      .slice()
      .reverse()
      .find(t => item.quantity >= t.min);
    const discount = tier ? tier.discount : 1;
    return total + product.basePrice * item.quantity * discount;
  }, 0);
}

这段代码看起来没问题,但我检查后发现两个隐藏 bug:

  • tiers 如果没有排序,.reverse() 会改变原数组引用;
  • 没有处理 max 字段为 undefined 的边界情况(最后一个阶梯通常只写 min)。

我让 AI 修正后,最终版本加了一层防御性处理:

javascript 复制代码
// src/utils/priceEngine.js
export function calculateQuote(items) {
  return items.reduce((total, item) => {
    const product = getProductById(item.productId);
    if (!product) throw new Error(`Unknown product: ${item.productId}`);

    const sortedTiers = [...product.tiers].sort((a, b) => b.min - a.min);
    const tier = sortedTiers.find(t => {
      const aboveMin = item.quantity >= t.min;
      const belowMax = t.max === undefined || item.quantity <= t.max;
      return aboveMin && belowMax;
    });

    const discount = tier ? tier.discount : 1;
    return total + product.basePrice * item.quantity * discount;
  }, 0);
}

这个例子说明,AI 能帮你快速写出 80% 的代码,但剩下 20% 的边界情况和业务规则,必须靠人来把关。我这次项目里总共让 AI 生成了大约 1200 行代码,我自己改动了大约 200 行,其中 150 行都是在处理边界情况。

四、PDF 导出:移动端场景下最折腾的一环

报价单最后要导出 PDF,而且要能在微信里长按保存。这个需求听起来简单,实际踩坑最多。

最初我尝试用纯前端方案 html2canvas + jspdf,在电脑上跑得很顺,一到手机上就出问题:中文字体缺失、表格换页错乱、iOS 上 canvas 尺寸超限导致白屏。

和 AI 讨论了三种方案:

  1. 客户端 canvas 方案:简单,但移动端兼容性差
  2. 服务端 Puppeteer 截图:需要后端,超出当前约束
  3. PDF-LIB 纯 JS 生成:不依赖 DOM 转图片,兼容性最好

最终选了 PDF-LIB。虽然它不支持直接写 HTML,但报价单结构固定,完全可以用代码"画"出来。AI 帮我封装了一个 generateQuotePDF 函数:

javascript 复制代码
// src/utils/pdfGenerator.js
import { PDFDocument, rgb, StandardFonts } from 'pdf-lib';

export async function generateQuotePDF(quote) {
  const pdfDoc = await PDFDocument.create();
  const page = pdfDoc.addPage([595, 842]); // A4
  const { width, height } = page.getSize();
  const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
  const fontSize = 12;

  let y = height - 50;
  page.drawText('客户报价单', { x: 50, y, size: 20, font });
  y -= 40;

  page.drawText(`客户:${quote.customer}`, { x: 50, y, size: fontSize, font });
  y -= 25;
  page.drawText(`日期:${quote.date}`, { x: 50, y, size: fontSize, font });
  y -= 40;

  quote.items.forEach((item, index) => {
    page.drawText(
      `${index + 1}. ${item.name} × ${item.quantity} = ¥${item.subtotal.toLocaleString()}`,
      { x: 50, y, size: fontSize, font }
    );
    y -= 25;
  });

  y -= 20;
  page.drawText(`合计:¥${quote.total.toLocaleString()}`, {
    x: 50,
    y,
    size: 14,
    font,
    color: rgb(0.8, 0.2, 0.2)
  });

  return await pdfDoc.save();
}

这个方案在 iOS 和 Android 上都能稳定运行,而且 PDF 文件大小只有几十 KB。唯一的代价是,样式需要自己用坐标"画",不像 HTML 转 PDF 那么直观。但对于这种固定格式的报价单,反而更可控。

五、部署与复盘:上线不是终点,而是迭代的起点

代码写完后,我把它推到了 GitHub,Vercel 自动完成了构建和部署。整个过程从 push 到线上可访问,花了 1 分 47 秒。

但这还不是结束。我把链接发给销售团队试用后,当天收到了 7 条反馈,其中 3 条是很有价值的:

  • 数量输入框在手机上太窄,容易误触
  • 折扣后价格没有显示"原价对比",客户不容易感知优惠
  • 希望报价单能加上公司 logo 和销售人员签名

这些需求都不复杂,但因为项目结构清晰,我每个改动都在 15 分钟内完成。比如显示原价对比,只需要在计算引擎里返回原始总价:

javascript 复制代码
export function calculateQuoteDetail(items) {
  return items.map(item => {
    const product = getProductById(item.productId);
    const tier = findTier(product, item.quantity);
    const originalSubtotal = product.basePrice * item.quantity;
    const subtotal = originalSubtotal * (tier?.discount ?? 1);
    return {
      ...item,
      originalSubtotal,
      subtotal,
      discount: tier?.discount ?? 1
    };
  });
}

这次经历让我意识到:AI 把「从 0 到 1」的时间压缩到了极致,但「从 1 到好」仍然需要真实的用户反馈和持续的微调。

总结

这次完全由 AI 辅助完成的小产品实战,给我留下几条很深的印象:

  1. AI 不能替代需求分析,但能帮你把模糊需求结构化。反问过你的问题越多,说明它理解得越到位。
  2. 约束条件是给 AI 最好的提示词。说"两天上线"比说"做一个报价系统"更能得到可执行的方案。
  3. AI 生成的代码一定要 Review 边界情况。80% 的功能可能一次对,但剩下 20% 的 bug 往往藏在数据为空、数组越界、移动端兼容这些细节里。
  4. 移动端优先要考虑真实设备。电脑上跑通的方案,到手机上可能就是另一回事,越早真机测试越好。
  5. 上线只是开始。小产品最大的价值在于快速拿到反馈、快速迭代,而不是一次性做到完美。

这次项目从需求到上线只花了 6 个小时,放在一年前是不可想象的。但真正让我省下来的时间,不是敲代码那几个小时,而是反复查文档、调样式、配环境这些低价值工作。人的价值,正在从"写代码"转向"定义问题、判断方案、验证结果"。

如果你也在尝试用 AI 做完整项目,我的建议是:不要问"AI 能不能做",而要问"在这个阶段,我该怎么和 AI 分工"。把合适的工作交给合适的对象,才是最优解。

相关推荐
无人生还1 小时前
从 Vue3 到 React · 快速上手系列第 6 篇:状态管理 useState 与 useReducer
前端·vue.js·react.js
名字还没想好☜1 小时前
Spring @Async 不生效排查:自调用失效、默认线程池坑与异步方法里的异常去哪了
java·python·spring·异步
hunterandroid1 小时前
Room 并发写入与事务一致性:从数据竞争到可靠落地
前端
天才熊猫君1 小时前
自动给所有 catch 块补上错误上报:从原理到落地
前端·javascript
wechatbot8881 小时前
解决企业微信官方 API 短板:iPad 协议全功能对接方案
java·汇编·windows·http·微信·企业微信
Tim_101 小时前
【C++】020、野指针&悬空指针
java·开发语言
朱涛的自习室1 小时前
Munk AI 桌面端「预告」
android·前端·人工智能
程序员包打听1 小时前
从 npx 到 moonx,moonbit 的野心与展望
前端·后端
laboratory agent开发1 小时前
工具调用失败后怎么办?重试分层与降级回路的三种路线
服务器·前端·网络