首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Day 2 实录:给 Picboil 装登录功能,AI 帮我埋了 5 个 Bug

Day 2 实录:给 Picboil 装登录功能,AI 帮我埋了 5 个 Bug

作者头像
袁锐钦
发布2026-04-16 16:15:58
发布2026-04-16 16:15:58
970
举报

袁锐钦 · AI编程实践者


录视频才发现!中间发一个超时的错误!!!

Day 1 把项目骨架和 CSS 洗衣机搞定了。

但现在的 Picboil 还是个玩具——谁都能用,没有用户概念,生成过的图片没地方存,免费次数没法控制。

Day 2 要干的事很明确:让玩具变成一个能用的产品。

三件事:

  1. 1. 登录注册 — 用户能注册和登录
  2. 2. 数据库 — 数据得有地方存
  3. 3. 修 Bug — AI 写的代码,不能直接信

第一步:登录功能——我选了 NextAuth

做网站需要登录,选项一大堆:

方案

优点

缺点

Clerk

开箱即用,UI漂亮

免费额度少,按量计费贵

自己写 JWT

完全可控

安全细节多,容易出漏洞

NextAuth.js v5

开源免费 + Next.js 原生集成

需要自己配

我选了 NextAuth.js v5(现在也叫 Auth.js)。

理由很实际:开源免费、Next.js 生态原生装上就能用、支持邮箱密码 + Google 登录等多种方式。MVP 阶段每一分钱都要算。

提示词模板: 如果你也要让 AI 帮你搭认证系统,可以这样下指令: "用 NextAuth.js v5 配置认证系统,支持 Credentials Provider(邮箱+密码)和 Google OAuth Provider,使用 JWT Session 策略。给出完整的配置代码和路由文件。"

配了什么

两种登录方式:

① 邮箱 + 密码登录

最传统的方式。用户填邮箱、设密码,密码用 bcrypt 哈希后存到数据库。没什么花哨的,就是稳。

② Google 一键登录

海外用户特别吃这一套——老外看到 "Continue with Google" 就放心了,不用记密码。

③ JWT Token 存登录状态

用户登录后发一张 JWT 卡片,下次访问自动验证身份。好处是部署简单(不用在服务器存 session),坏处是注销没那么即时。MVP 够用了。

AI 帮我把这套配通之后测试了一下——邮箱注册 ✅,Google 登录 ✅,退出再进 ✅。

看起来没问题。

然后问题来了。


第二步:数据库——4 张表管住所有数据

选了 Prisma ORM + PostgreSQL

什么是 ORM? 简单说就是"翻译官"——你用 JavaScript 写代码,ORM 帮你翻译成数据库能听懂的 SQL 语句。不用手写 SQL,不容易出错。

为什么选 Prisma?它是 Next.js 生态里最成熟的 ORM,AI 写 Prisma 代码也最顺手,文档全、社区大。

设计了 4 张表:

代码语言:javascript
复制
users     → 用户信息(邮箱、密码哈希、名字)
credits   → 积分余额(每天免费几次、已用几次、付费余额)
tasks     → 生成任务记录(提示词、参数、状态)
images    → 生成的图片记录(图片地址、大小、关联任务)

一个关键设计决策:软删除模式

每张表都加了 deleted_at 字段。删除数据不是真删,只是打个时间戳标记"这条已删除"。

为什么?

  1. 1. 可以恢复误删 — MVP 阶段手抖很正常
  2. 2. 数据审计 — 出海后可能面临 GDPR 之类的合规要求
  3. 3. 关联完整 — 用户删了,他的图片记录还在,不会出空指针报错

AI 写好 schema,npx prisma migrate dev 一跑,PostgreSQL 里 4 张表整整齐齐建好了。

看起来也没问题。

然后问题真的来了。


第三步:5 个 Bug——今天最想跟你分享的部分

前面都是铺垫,这里才是干货。

Bug 1:AI 默认装了最新版 Prisma,结果炸了

现象: 配置完启动项目报错,提示数据库连接字符串有问题。明明 .env 文件里写了连接地址,但运行时读不到。

原因: AI 默认给我装了 Prisma v7(最新版)。但 v7 有很多破坏性更新,其中之一就是废弃了旧版的数据库配置方式,改成运行时动态获取。结果各种不兼容。

怎么修:

代码语言:javascript
复制
# 直接降级到稳定版
npm install prisma@5 @prisma/client@5

不是我不想用新版,而是 MVP 阶段我的优先级是能跑,不是追新。v5 稳定、文档全、AI 写代码也靠谱。等以后产品稳定了再考虑升级。

💡 提示词技巧: 让 AI 装依赖时,可以加一句"使用稳定版本而非最新版",或者直接指定版本号如 "使用 prisma@5"。能省掉很多兼容性坑。

教训:AI 总爱装最新版,但最新版不一定最稳定。锁定版本很重要。


Bug 2:用户疯狂点按钮,免费次数被白嫖了

这个最有意思,值得展开讲。

现象: 我设置了每个用户每天免费生成 5 张图。但快速连点两次生成按钮,有时候能用 6 次、甚至 7 次。

为什么?

这是一个经典的竞态条件(Race Condition)

通俗解释: 想象一个只有1个窗口的票务处。两个人同时冲进去买最后一张票。工作人员 A 查了一下"还有1张",工作人员 B 也查了"还有1张",然后两人都把票卖出去了。多卖了1张,航空公司亏了。

我们的系统也一样:

请求A(第一次点击)

请求B(第二次点击)

读数据:已用 4 次

读数据:已用 4 次

判断:4 < 5,没超限 ✅

判断:4 < 5,没超限 ✅

+1 → 变成 5 次

+1 → 变成 6 次!

两个请求同时读到 4,都判断没超限,都加了 1。用户白嫖了一次。

如果这是付费积分呢?用户本来只有 10 个积分,并发请求可能只扣了 1 次的钱就生成了 2 次。直接亏钱。

怎么修:

用数据库的原子操作——让"读取并加1"变成不可分割的一步:

代码语言:javascript
复制
// ❌ AI 写的原始代码(有 bug)
const user = await prisma.user.findUnique({ where: { email } });
if (user.free_used_today < 5) {
  await prisma.user.update({
    where: { email },
    data: { free_used_today: user.free_used_today + 1 }
  });
}

// ✅ 修复后(原子操作,不会出问题)
await prisma.$transaction([
  prisma.credits.update({
    where: { userId },
    data: { free_used_today: { increment: 1 } }
  })
]);

{ increment: 1 } 是 Prisma 的原子操作符。数据库保证这个操作不可分割,两个请求进来也会排队处理,一个一个来。

💡 提示词技巧: 涉及积分/余额/库存等数值操作时,在 prompt 里加一句"注意处理并发安全,使用数据库原子操作或事务"。AI 默认写的代码不考虑并发,因为它默认世界是串行的——但真实用户会连点、会刷新、会同时开多个标签页。

教训:任何涉及"先读再改"的操作都要考虑并发。


Bug 3:用 Google 登录后,之前的账号"消失"了

现象: 先用邮箱注册了一个账号(有免费积分),然后用同一个 Google 账号登录——结果系统把它当成了新用户,或者更糟,积分没了。

原因: AI 写的认证逻辑只处理了两种情况:

  • • 全新用户(从来没见过)→ 创建账户 + 发积分
  • • 已有用户用同方式登录(比如都用邮箱)→ 正常登录

但它漏掉了第三种情况

用户先用邮箱注册了,后来改用 Google 登录。邮箱是同一个,但登录方式不同(一个是邮箱密码,一个是 Google)。系统该怎么处理?

AI 的原始代码在 Google 回调里只查了 where: { email, provider: 'google' }。如果这个用户之前是用邮箱注册的,Google 登录时就查不到他——要么报错,要么给他建了个重复账户,积分归零。

怎么修:

登录时先查所有方式下有没有这个邮箱,有就直接放行,没有才创建新的:

代码语言:javascript
复制
// 不只查 google provider,查所有
const existingUser = await prisma.user.findFirst({
  where: { email: googleEmail }  // ← 不限定 provider
});

if (existingUser) {
  // 老用户换了一种登录方式,直接放行
  return existingUser;
} else {
  // 真的新用户,创建 + 发积分
  return await createUser(googleEmail, &#x27;google&#x27;);
}

💡 提示词技巧: 让 AI 写认证逻辑时,加上"处理多 provider 账号合并场景:同一邮箱通过不同方式注册/登录时,应关联到同一账户而非创建重复账户"。

教训:认证系统的边界情况比主流程还多。正常流程能跑通不代表没问题——你得想:换一种登录方式会怎样?先登 A 再登 B 会怎样?过期了再来会怎样?


Bug 4:用户输入乱填邮箱,系统傻乎乎地照单全收

现象: 用户输入 "hello" (根本没有 @ 符号),系统拿去加密存储,白白消耗算力。

为什么这是个问题:

  1. 1. 浪费算力 — bcrypt 加密是故意设计得很慢的(防暴力破解),对一个无效邮箱做哈希白白耗几十毫秒
  2. 2. 垃圾入库 — "hello" 这种字符串存进数据库,后面查询分析全是噪音
  3. 3. 体验差 — 用户打错邮箱地址,注册"成功"了但收不到任何邮件

怎么修:

加一行格式校验,不合法的直接拦回去:

代码语言:javascript
复制
// 先校验格式,通过再走加密
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
  throw new Error(&#x27;邮箱格式不正确&#x27;);
}
// 校验通过才走 bcrypt
const hashedPassword = await bcrypt.hash(password, 12);

💡 提示词技巧: 在注册/登录相关需求里加一条"所有用户输入必须先做格式校验再进行后续处理(bcrypt/数据库写入/API调用)"。这几乎是免费的防护,但 AI 经常忘记做。

教训:先验证再处理,别让无效数据进入流水线。 这跟工厂生产一样——原料不合格就不该送进车间,不然产出的全是废品,还得花成本返工。


Bug 5:代码里到处是神秘数字 "5"

现象: 代码里写着各种孤零零的 5

代码语言:javascript
复制
if (user.free_used_today >= 5) return error;
// ...
const DAILY_LIMIT = 5;
// ...
<span>剩余 {5 - user.free_used_today} 次</span>

今天我记得 5 是"每天免费生成次数"。但下周呢?三个月后呢?或者别的同事来看这段代码呢?

他们只会看到一个孤零零的 5,不知道这是什么意思。如果想改成每天 3 次呢?得搜遍整个代码库把所有 5 改掉——万一有些 5 不是指免费次数呢?改错了就出新 bug。

怎么修:

抽成常量,一处定义全局引用:

代码语言:javascript
复制
// constants.ts
export const DEFAULT_FREE_DAILY = 5;

// 所有地方统一用常量
if (user.free_used_today >= DEFAULT_FREE_DAILY)
const remaining = DEFAULT_FREE_DAILY - user.free_used_today

💡 提示词技巧: "代码中所有业务相关的数字必须定义为命名常量(如 FREE_DAILY_LIMIT),禁止出现 magic number。" 一句话就能避免这个问题。

教训:魔法数字是维护噩梦的开始。 今天你觉得"这很明显是什么意思",三个月后你会感谢当初写了注释的自己。


第四步:Stripe 注册

这部分今天没有太多可讲的——在 Stripe 官网注册了账号,拿到了 API 测试密钥。

作为出海站,支付是必须的。Picboil 的商业模式是 Freemium(免费增值):免费用户每天有限额,付费用户解锁更多功能和更高额度。

Stripe 接入留到后面再做,今天先把账号和基础环境搞定。


Day 2 总结

今天完成了什么

✅ NextAuth.js v5 认证系统(邮箱 + Google OAuth) ✅ Prisma + PostgreSQL 数据库(4张表,软删除模式) ✅ 积分系统的原子操作修复(并发安全) ✅ Google OAuth 多账户关联修复 ✅ 邮箱格式前置校验 ✅ 魔法数字常量化 ✅ Stripe 注册 + 测试环境配置

今天的花费

项目

花费

时间

约 8 小时

金钱

$0(全部用免费额度 / 开源方案)

新增依赖

NextAuth + Prisma + PostgreSQL + bcrypt

修复 Bug 数

5 个

一个重要的体会

Day 1 教会我的是"不要过度工程化",Day 2 教会我的是"AI 写的代码不能直接信"。

5 个 Bug 里,有 3 个是 AI 写代码时的"典型毛病":

  • 不考虑并发(Bug 2)— 它默认世界是串行的
  • 不考虑边界情况(Bug 3)— 只处理了 happy path
  • 不注意代码质量细节(Bug 4、Bug 5)— 能跑就行

AI 能帮你写出能跑的代码,但能不能在生产环境稳定运行,取决于你自己有没有审查意识

我不是程序员出身,但我正在学会一件事:

不要对着 AI 生成的代码说"看起来没问题",而要问自己"如果我是用户,我会怎么搞崩它"。

从这个角度思考,今天这 5 个 Bug 每一个都有价值——因为它们都是在上线前发现的,总比上线后被用户发现好。


当前进度(截至写稿时)

到今天这篇文章的时候,实际上 Day 3 的活已经干掉大半了:

滚筒转动与 AI 生成的衔接 — 动画结束 → 调 Gemini API → 图片出来 ✅ 生成历史展示 — 用户能看到之前生成过的图 🔲 便签纸飞进洗衣机的动画序列 — 从输入到生成的完整交互链路(待完善)

三天下来,Picboil 已经是一个能完整走通"注册→登录→输入→生成→查看"流程的最小可用产品了。

下一步就是打磨交互体验,然后准备上线测试。


如果你在做 AI 辅助开发,或者对这些踩坑经历有共鸣,欢迎交流。

一个人走得快,一群人走得远。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Ruiqin袁锐钦 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第一步:登录功能——我选了 NextAuth
    • 配了什么
  • 第二步:数据库——4 张表管住所有数据
  • 第三步:5 个 Bug——今天最想跟你分享的部分
    • Bug 1:AI 默认装了最新版 Prisma,结果炸了
    • Bug 2:用户疯狂点按钮,免费次数被白嫖了
    • Bug 3:用 Google 登录后,之前的账号"消失"了
    • Bug 4:用户输入乱填邮箱,系统傻乎乎地照单全收
    • Bug 5:代码里到处是神秘数字 "5"
  • 第四步:Stripe 注册
  • Day 2 总结
    • 今天完成了什么
    • 今天的花费
    • 一个重要的体会
  • 当前进度(截至写稿时)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档