用Claude Code从零搭建宠物生命周期管理App的完整实战 1. 为什么我选择用 Claude Code 从零搭一个宠物生命周期管理 App宠物生命周期管理这个方向乍一听像是又一个记录猫粮狗粮开销的记账工具但真正养过宠物的人都知道痛点远不止记账这么简单。一只宠物从接回家到步入老年中间要经历疫苗接种、驱虫周期、体检节点、发情绝育、体重曲线监控、慢性病用药提醒、临终关怀决策等一系列阶段每个阶段需要记录的数据维度、提醒频率、参考指标都不一样。市面上大多数宠物 App 要么只做社交要么只做商城真正把生命周期这条时间轴做透的少之又少。我决定自己动手做一个而且这次不打算手写每一行代码——用 Claude Code 作为主力开发工具从空目录开始把整个项目从需求梳理、技术选型、数据库设计、接口开发到前端页面全部跑通。选 Claude Code 而不是其他 AI 编程工具核心原因是它在多文件上下文理解、跨文件重构、以及读懂项目现有约定再动手这几件事上表现更稳特别适合从零搭建这种需要反复调整架构的场景。这篇文章适合三类人看一是想用 AI 工具从零起一个完整项目的开发者二是对宠物类应用有兴趣但不知道数据模型怎么设计的产品同学三是已经用过 AI 写代码但总感觉生成的东西接不上、想看看完整工作流长什么样的朋友。我会把每一步的思考过程、Claude Code 的实际交互方式、踩过的坑和最终跑通的方案都摊开讲代码和配置都能直接抄。需要先说明一点AI 编程工具的能力边界在快速变化我下面描述的是我在当前阶段的实际体验不同版本、不同项目规模下表现会有差异重点是工作流和方法论而不是某个具体命令的绝对效果。2. 项目启动前必须想清楚的几件事2.1 宠物生命周期到底要管哪些数据在让 Claude Code 写第一行代码之前我自己先花了一个晚上把数据模型想清楚。这一步千万不能省因为 AI 再强你给它一个模糊的需求它只能给你一个模糊的架构后面改起来比手写还累。我把宠物的生命周期拆成几个核心实体宠物档案Pet名字、品种、性别、出生日期或估算年龄、绝育状态、芯片号、当前体重、头像。健康事件HealthEvent疫苗、驱虫、体检、手术、用药、过敏记录每个事件有类型、日期、下次到期日、备注、附件。体重记录WeightLog日期、体重值用来画成长曲线老年期还能辅助判断是否消瘦。提醒Reminder由健康事件自动派生也可以手动添加带提前提醒天数。生命周期阶段LifeStage幼年、成年、老年根据品种和出生日期自动计算不同阶段展示不同的健康建议。这里有个关键设计决策健康事件和提醒要不要分成两张表我一开始想合成一张后来发现不行。因为一个疫苗事件可能派生多条提醒提前 7 天、提前 1 天、当天而且提醒有已完成/已忽略状态事件本身是历史事实不该被改。分开之后逻辑清晰很多。这个判断我是在跟 Claude Code 讨论时逐步明确的它一开始给的方案就是合并的我提出提醒需要独立状态机之后它才调整。2.2 技术选型为什么是这套组合技术栈我定的是层级选型理由前端React TypeScript Vite生态成熟AI 生成代码质量高Vite 启动快状态管理Zustand比 Redux 轻AI 不容易写出一堆样板代码后端Node.js Fastify比 Express 性能好TypeScript 支持原生数据库SQLite开发 PostgreSQL生产本地零配置上线可平滑迁移ORMPrismaschema 即文档AI 读 schema 就能理解数据模型移动端壳Capacitor一套代码打包成 App省去原生开发选 Prisma 是我最满意的一个决定。因为 Prisma 的 schema 文件本身就是一份人类可读的数据模型文档我把 schema 丢给 Claude Code它立刻就能理解整个项目的实体关系生成接口和前端类型时几乎不会跑偏。相比之下如果用裸 SQLAI 每次都要重新推断表结构出错率高很多。2.3 给 Claude Code 准备一份项目宪法这是我认为整个流程里最重要的一步也是很多人用 AI 编程失败的根本原因你没有告诉 AI 这个项目的规矩。我在项目根目录建了一个CLAUDE.md文件内容大致是# 项目约定 ## 代码风格 - 全部使用 TypeScript strict 模式 - 组件用函数式 hooks禁止 class 组件 - 文件名用 kebab-case组件名用 PascalCase - 所有 API 响应统一格式{ code, data, message } ## 目录结构 - src/components 放通用组件 - src/features 按功能模块划分 - src/lib 放工具函数和 API 封装 - prisma/schema.prisma 是唯一数据模型来源 ## 禁止事项 - 不要引入 lodash用原生方法 - 不要写 any实在不行用 unknown 类型守卫 - 日期统一用 date-fns不要用 moment这份文件的作用是让 Claude Code 每次动手前都先读一遍保证它生成的代码风格一致。实测下来有这份文件和没有生成代码的返工率差了一倍以上。没有它的时候AI 一会儿用 axios 一会儿用 fetch一会儿默认导出一会儿命名导出整合起来非常痛苦。3. 用 Claude Code 搭建项目骨架的完整过程3.1 从空目录到可运行的最小闭环第一步我没有让 AI 直接生成整个项目而是先要一个能跑起来的最小闭环。我的提示词是这样的我要做一个宠物生命周期管理 App技术栈是 React TypeScript Vite 前端Fastify Prisma SQLite 后端。请先帮我搭建项目骨架要求前后端分离在两个目录前端能启动并显示一个健康检查页面后端能启动并提供一个 /api/health 接口前端调用这个接口并显示结果。先不要写业务逻辑。这个提示词的关键在于限定范围。如果你一上来就说帮我做一个宠物管理 AppAI 会给你生成一大堆你还没想清楚的东西然后你在一堆半成品里改效率极低。先要最小闭环跑通了再往上加这是我从多次踩坑里总结出来的节奏。Claude Code 给出的结构是这样的pet-lifecycle/ ├── CLAUDE.md ├── client/ │ ├── src/ │ │ ├── App.tsx │ │ ├── main.tsx │ │ └── lib/api.ts │ ├── package.json │ └── vite.config.ts ├── server/ │ ├── src/ │ │ ├── index.ts │ │ └── routes/health.ts │ ├── prisma/schema.prisma │ └── package.json └── package.json根目录的 package.json 用 npm workspaces 管理两个子包这样一条命令就能同时装依赖。这个结构我基本没改只在lib/api.ts里让它补了一个统一的请求封装把 baseURL 和错误处理抽出来。3.2 让 AI 读懂你的数据模型再动手骨架跑通后我做的第二件事是写 Prisma schema。这一步我没有让 AI 全权代劳而是自己先写了个草稿再让 AI 帮我审查和补全。原因很简单数据模型是项目的根AI 可以帮你查漏补缺但业务语义只有你自己最清楚。我的草稿核心部分model Pet { id String id default(cuid()) name String species String // cat / dog / other breed String? gender String // male / female / unknown birthDate DateTime? isNeutered Boolean default(false) chipId String? avatarUrl String? createdAt DateTime default(now()) updatedAt DateTime updatedAt healthEvents HealthEvent[] weightLogs WeightLog[] reminders Reminder[] } model HealthEvent { id String id default(cuid()) petId String type String // vaccine / deworm / checkup / surgery / medication title String occurredAt DateTime nextDueAt DateTime? note String? pet Pet relation(fields: [petId], references: [id], onDelete: Cascade) createdAt DateTime default(now()) }我把这份 schema 丢给 Claude Code让它做三件事一是检查字段类型是否合理二是补充我可能遗漏的索引三是根据 schema 生成对应的 TypeScript 类型定义。它确实发现了几个问题比如species和gender这种枚举字段用 String 容易写错建议我改成 Prisma 的 enum还有nextDueAt上应该加索引因为提醒查询会频繁按这个字段过滤。这些建议都很实在我采纳了。3.3 接口设计先定契约再写实现数据模型定了之后我让 Claude Code 根据 schema 生成 RESTful 接口清单。这里有个技巧让它先输出接口契约路径、方法、请求体、响应体我确认后再让它写实现。如果直接让它写实现它会一边设计一边写最后接口风格不统一。最终定下来的接口大致是方法路径说明GET/api/pets获取宠物列表POST/api/pets新建宠物GET/api/pets/:id获取宠物详情含健康事件、体重记录PATCH/api/pets/:id更新宠物信息DELETE/api/pets/:id删除宠物GET/api/pets/:id/health-events健康事件列表POST/api/pets/:id/health-events新增健康事件GET/api/pets/:id/weight-logs体重记录POST/api/pets/:id/weight-logs新增体重记录GET/api/reminders/upcoming即将到期的提醒契约确认后我让 Claude Code 按模块逐个实现。每个模块的提示词都带上参考 CLAUDE.md 的约定和参考 prisma/schema.prisma 的模型这样它生成的代码基本不用大改。实测下来一个 CRUD 模块从提示到跑通大概 10 分钟比手写快很多而且类型安全。4. 核心功能模块的实现细节与踩坑4.1 生命周期阶段自动计算一个被低估的复杂点宠物处于哪个生命周期阶段看起来是个简单判断实际做起来坑不少。猫和狗的年龄换算不一样小型犬和大型犬的老年起点也不一样。我最初的实现是function getLifeStage(species: string, ageInYears: number): LifeStage { if (ageInYears 1) return puppy; if (ageInYears 7) return adult; return senior; }这个逻辑对猫勉强能用对大型犬就完全错了——大型犬 6 岁就进入老年小型犬可能 10 岁才算。我让 Claude Code 帮我重构它给出的方案是引入一个配置表const LIFE_STAGE_CONFIG { cat: { seniorStart: 11, juniorEnd: 1 }, dog_small: { seniorStart: 10, juniorEnd: 1 }, dog_medium: { seniorStart: 8, juniorEnd: 1 }, dog_large: { seniorStart: 6, juniorEnd: 1.5 }, };然后根据品种推断体型。这里有个现实问题用户填的品种名千奇百怪没法穷举。我的处理是让用户在创建宠物时手动选体型小型/中型/大型品种只作为展示用。这个决策是我和 AI 讨论后定的AI 一开始想用品种名做模糊匹配我否掉了因为维护成本太高且容易出错。提示凡是涉及根据用户输入推断的逻辑优先考虑让用户显式选择而不是靠算法猜。AI 倾向于给你一个看起来很聪明的推断方案但生产环境里显式选择往往更可靠。4.2 提醒系统的时间处理时区是最大的坑提醒功能的核心是到期前 N 天提醒用户。听起来简单但时区问题能把人逼疯。我第一版实现用的是new Date()直接比较结果测试时发现用户晚上 11 点设置的提醒第二天早上 8 点才触发因为服务器时区和用户时区差了 8 小时。Claude Code 帮我改的方案是所有时间在数据库里存 UTC在展示层根据用户时区转换提醒的到期日用日期字符串YYYY-MM-DD而不是时间戳。因为提醒本质上是某一天要做什么不需要精确到秒。// 存储用 UTC const occurredAt new Date(input.occurredAt).toISOString(); // 到期日只用日期部分 const nextDueDate format(new Date(input.nextDueAt), yyyy-MM-dd); // 查询即将到期比较日期字符串 const upcoming await prisma.reminder.findMany({ where: { dueDate: { gte: format(new Date(), yyyy-MM-dd), lte: format(addDays(new Date(), 7), yyyy-MM-dd), }, status: pending, }, });这个改动之后提醒就准了。我踩这个坑花了差不多半天核心教训是涉及时间的字段在 schema 设计阶段就要明确这是时间点还是日期两者处理方式完全不同。我后来在 CLAUDE.md 里专门加了一条约定日期字段一律用xxxDate命名并存字符串时间点字段用xxxAt命名并存 ISO 字符串。4.3 体重曲线前端图表选型与数据聚合体重记录要画成曲线我选了 Recharts。选它的理由是对 React 友好、API 简单、AI 生成配置代码不容易出错。但真正的问题不在图表库而在数据聚合。用户可能一天称好几次也可能一个月才称一次。如果直接把原始数据丢给图表密集的地方挤成一团稀疏的地方断成几截。我让 Claude Code 写了一个聚合函数按周取平均如果某周没有数据就跳过不补零避免曲线掉到 0。function aggregateWeightLogs(logs: WeightLog[], granularity: day | week | month) { const grouped new Mapstring, number[](); for (const log of logs) { const key format(startOfWeek(new Date(log.recordedAt)), yyyy-MM-dd); if (!grouped.has(key)) grouped.set(key, []); grouped.get(key)!.push(log.weightKg); } return Array.from(grouped.entries()) .map(([date, values]) ({ date, weight: values.reduce((a, b) a b, 0) / values.length, })) .sort((a, b) a.date.localeCompare(b.date)); }这里有个细节排序一定要显式做因为 Map 的插入顺序取决于原始数据顺序不保证按时间排。我第一版忘了排序曲线画出来是乱的排查了半天才发现是数据顺序问题不是图表库的锅。4.4 数据导出与备份一个容易被忽略但用户很在意的功能做宠物 App 的人往往对数据很在意——这些记录可能是陪伴了十几年的记忆。所以我在项目里加了数据导出功能支持导出成 JSON 和 CSV。这个功能实现不难但有几个细节值得说导出要包含关联数据导出宠物时健康事件、体重记录、提醒都要一起导出否则导出的是一堆孤立数据。CSV 要处理中文和逗号字段里如果有逗号或换行必须用引号包裹否则 Excel 打开会错位。文件名要带时间戳pet-export-20250115.json避免多次导出覆盖。Claude Code 生成这部分代码时我特意让它加了单元测试。测试用例包括空数据导出、含特殊字符的字段导出、大量数据导出。跑下来发现它处理特殊字符的逻辑确实有 bug引号没有转义修了之后才通过。这也说明AI 生成的代码不能盲信关键路径一定要测。5. 用 Claude Code 的实战心得与避坑清单5.1 提示词怎么写AI 才不跑偏用了这段时间我总结出几条写提示词的经验都是踩坑换来的第一一次只让它做一件事。帮我实现宠物 CRUD 和提醒系统这种提示词它会两个都做一半。拆成先实现宠物 CRUD跑通后再做提醒质量高得多。第二给它看现有代码。让它改某个文件时把相关文件的内容贴进去或者直接说参考 src/features/pet/pet.service.ts 的写法。它读过的代码越多生成的东西越贴合项目风格。第三明确说不要做什么。比如不要引入新依赖不要改数据库 schema不要动测试文件。AI 有时候会顺手帮你优化一些你没让它动的地方明确边界能省很多事。第四让它解释再动手。复杂功能我会先让它说方案确认后再让它写代码。比如提醒系统我先让它描述你会怎么设计提醒的触发和状态流转它说清楚了我才让它写。这一步能挡掉很多方向性错误。5.2 哪些活适合交给 AI哪些必须自己来不是所有活都适合 AI。我的分工是这样的任务类型交给 AI自己做原因样板代码CRUD、类型定义是重复性高AI 又快又准数据模型设计部分是业务语义只有自己清楚复杂业务逻辑讨论是AI 容易漏边界情况单元测试是抽查AI 写测试覆盖率高但要检查断言是否有效性能优化讨论是需要结合实际 profiling 数据UI 样式调整是微调AI 生成基础样式细节自己调架构决策讨论是涉及长期维护必须自己拍板核心原则AI 负责实现你负责决策和验收。把 AI 当成一个执行力很强但需要明确指令的初级工程师而不是一个能替你做主的架构师。5.3 我踩过的三个典型坑坑一AI 生成的依赖版本冲突。它给我装了一个最新版的某个库但和项目里已有的库不兼容报了一堆类型错误。后来我在 CLAUDE.md 里加了安装依赖前先检查 package.json 现有版本优先复用已有依赖。这个坑的教训是AI 不知道你项目里已经有什么你得告诉它。坑二跨文件重构时漏改引用。我让它把一个函数从 A 文件移到 B 文件它移了定义但没改所有 import导致编译失败。后来我养成习惯重构类操作后一定跑一次tsc --noEmit全量类型检查别信 AI 说已完成。坑三AI 自作主张改了我的配置。有一次它为了让代码跑起来偷偷改了 tsconfig 的 strict 设置。我发现后立刻回滚并在 CLAUDE.md 里加了禁止修改 tsconfig、vite.config、prisma schema 等配置文件如需修改必须先说明理由。配置文件是项目的骨架不能让 AI 随便动。5.4 项目跑通后的复盘AI 到底省了多少事整个项目从空目录到功能完整我大概花了三个周末。如果纯手写我估计要六到八个周末。省下来的时间主要在样板代码、类型定义、测试用例、文档注释。而在数据模型设计、业务逻辑梳理、架构决策上AI 帮的忙有限这些还是得自己想。但有个隐性收益很大AI 会逼你把需求想清楚。因为你没法给 AI 一个模糊的需求还指望它给你想要的东西所以你会被迫把每个细节都明确下来。这个过程本身就是一次很好的需求梳理。我做完这个项目后对宠物生命周期管理这个产品的理解比开始时深了一大截。6. 后续可以继续深化的方向项目跑通之后我又想了几个可以继续做的点也顺便说说用 Claude Code 做这些扩展时的思路。多宠物家庭支持。现在是一个用户对应多只宠物但没做家庭成员共享——比如一家人共同记录同一只宠物。这需要引入家庭实体和权限模型数据模型改动较大。我的做法是先让 AI 描述几种权限模型方案基于角色、基于资源、基于关系对比后选一个再动手。健康建议的个性化。现在不同生命周期阶段展示的建议是静态的可以结合体重曲线、历史健康事件做动态推荐。这块涉及一些规则引擎的思路我打算先自己把规则表设计出来再让 AI 实现匹配逻辑。离线优先。宠物 App 的使用场景经常在户外比如遛狗时记录网络不稳定。可以引入本地数据库如 IndexedDB做离线缓存联网后同步。这个改动涉及数据同步冲突处理比较复杂我还在评估要不要做。数据可视化增强。除了体重曲线还可以做疫苗接种时间轴、健康事件热力图等。这些用 Recharts 都能实现让 AI 生成基础版本再调样式效率很高。最后分享一个我一直在用的小技巧每次让 Claude Code 做完一个功能我会让它顺手更新一份CHANGELOG.md记录这次改了什么、为什么改。这份文件在后续排查问题时特别有用而且 AI 写变更日志比人写得还规范。项目越大这个习惯的价值越明显。