首页 / 资讯中心 / 文章详情

AI全流程搭建宠物生命周期管理App:React Native与Supabase实战

AI全流程搭建宠物生命周期管理App:React Native与Supabase实战 ★ FEATURED ARTICLE
1. 为什么我选择用AI全流程搭建一个宠物生命周期管理App先说结论这个项目我从零到可运行原型前后花了不到三天其中真正写业务代码的时间大概只占三成剩下的时间全花在“怎么让AI理解我要什么”上。宠物生命周期管理App说白了就是记录一只宠物从接回家、日常喂养、疫苗驱虫、体检看病、到老年护理甚至离世的全过程管理工具。市面上这类产品不少但大多功能割裂——有的只管疫苗提醒有的只管记账有的只管健康档案。我想做的是一个真正贯穿“生命周期”这个概念的东西让养宠人打开一个App就能看到这只宠物当前处于哪个阶段、接下来该做什么、过去发生过什么。为什么用Claude Code来做因为这类App的业务逻辑其实不复杂复杂的是数据模型的设计和状态流转的梳理。宠物生命周期本质上是一个状态机幼年期、成年期、老年期每个阶段对应不同的护理重点和提醒策略。这种“逻辑密集但代码量中等”的项目恰好是AI辅助开发最擅长的场景。你不需要它帮你写一个高并发的分布式系统你需要的是它帮你把业务逻辑理清楚、把数据模型设计好、把CRUD代码快速生成出来。这篇文章适合谁看如果你是一个有一定编程基础、想用AI工具快速把想法落地成原型的开发者或者你是一个产品经理/独立开发者想了解AI辅助开发的真实工作流和踩坑经验那这篇内容应该对你有用。我会把整个流程拆开讲——从需求梳理、技术选型、数据模型设计、核心功能实现、到最后的调试和优化每一步都会说清楚我为什么这么做、遇到了什么问题、怎么解决的。需要提前说明的是下面涉及的所有项目名称、机构名称、人物名称均为虚构代称仅用于说明流程。实际开发中请根据你自己的情况调整。2. 项目整体设计与技术选型思路2.1 为什么选这个技术栈而不是别的宠物生命周期管理App的技术选型我考虑过三个方案。方案一是纯Web应用用ReactNode.js优点是开发快、部署简单缺点是移动端体验差而养宠人恰恰是最需要移动端提醒的人群。方案二是原生AppiOS和Android各写一套优点是体验最好缺点是开发周期太长一个人根本扛不住。方案三是跨平台方案用React Native或者Flutter一套代码两端跑体验接近原生开发效率也高。最终我选了React NativeExpo的组合。原因很实际Expo提供了大量开箱即用的API比如推送通知、相机、文件存储这些在宠物管理App里都是刚需——你要拍照记录宠物状态、要上传疫苗本照片、要设置提醒推送。如果用纯React Native光是配置这些原生模块就要花掉大量时间。Expo把这些都封装好了你直接调用就行。后端方面我没有用传统的REST API关系型数据库而是选了Supabase。Supabase本质上是一个开源的Firebase替代品提供PostgreSQL数据库、认证、实时订阅、文件存储、边缘函数等能力。为什么选它因为宠物生命周期管理App的数据模型天然适合关系型数据库——宠物、主人、疫苗记录、体检记录、提醒事项这些实体之间有明确的外键关系。同时Supabase的实时订阅功能可以让我在多个设备之间同步数据比如你在手机上添加了一条疫苗记录平板上的数据会自动更新。AI辅助开发工具方面我用的是Claude Code。它的优势在于对项目上下文的理解能力比较强你可以在对话中逐步构建整个项目它会记住之前的设计决策。而且它支持直接操作文件系统可以帮你创建文件、修改代码、运行命令整个流程比较顺畅。2.2 数据模型设计的核心考量宠物生命周期管理App的数据模型我反复改了三次。第一版设计得太细把每个护理动作都拆成独立实体结果表太多、关联太复杂写查询的时候自己都绕晕了。第二版又太粗把健康记录、疫苗记录、体检记录全塞在一张表里用JSON字段存差异化的内容结果查询和统计极其困难。第三版才找到平衡点。核心实体有这几个用户User、宠物Pet、健康记录HealthRecord、提醒Reminder、护理计划CarePlan。用户和宠物是一对多关系一个用户可以有多只宠物。宠物和健康记录是一对多关系每条健康记录属于一只宠物。提醒和护理计划都关联到宠物但提醒是具体的待办事项护理计划是模板化的规则。这里有个关键设计决策健康记录用了一张表但通过type字段区分类型疫苗、驱虫、体检、用药、手术等差异化的字段用JSONB存储。比如疫苗记录需要记录疫苗名称、批号、接种机构体检记录需要记录体重、体温、各项指标这些差异化内容放在JSONB里通用字段时间、备注、附件放在普通列里。这样既保证了查询效率又保留了灵活性。为什么不用多张表因为宠物健康记录的类型会随着业务发展不断增加每加一种类型就加一张表维护成本太高。而且很多查询需要跨类型聚合比如“显示这只宠物过去一年的所有健康事件”多表查询写起来很痛苦。用单表JSONB的方案一个查询就能搞定。2.3 AI辅助开发的边界在哪里用Claude Code做这个项目我最大的体会是AI非常擅长“有明确输入输出的逻辑实现”但不太擅长“模糊需求的澄清和决策”。什么意思比如你告诉它“写一个函数输入宠物ID和日期范围返回这段时间内的所有健康记录按时间倒序排列”它能立刻给你一个正确的实现。但如果你告诉它“帮我设计一个宠物健康管理的数据模型”它给出的方案往往过于通用缺乏对具体业务场景的深入理解。所以我的工作流是我自己先想清楚要什么把需求拆解成具体的、可验证的任务然后让AI去实现。AI在这个过程中扮演的是一个“执行力极强但需要明确指令的初级开发者”角色。你不能指望它帮你做产品决策但你可以指望它帮你把决策快速变成代码。还有一个边界是调试。AI能帮你发现一些明显的bug比如变量未定义、类型不匹配但对于运行时才暴露的逻辑错误比如“为什么这个提醒没有按时触发”它往往需要你提供详细的日志和上下文才能定位。我的做法是遇到问题时先把错误信息、相关代码、我的预期行为整理清楚再让AI分析。这样效率最高。3. 核心功能模块的详细拆解与实操要点3.1 宠物档案模块从录入到展示的完整链路宠物档案是整个App的基础。没有宠物档案后面的健康记录、提醒、护理计划都无从谈起。这个模块的核心功能包括添加宠物、编辑宠物信息、查看宠物详情、切换当前宠物。添加宠物的表单字段我设计了这些名字必填、物种猫/狗/其他、品种、性别、生日或大致年龄、绝育状态、体重、头像、备注。这里有个细节生日和年龄的取舍。很多用户不记得宠物的确切生日只知道大概几岁。所以我把生日字段设计成可选的如果用户不填生日但填了年龄系统会自动推算一个大概的出生日期并在展示时标注“约”。头像上传用的是Expo的ImagePicker拍完照后压缩到合适尺寸再上传到Supabase Storage。这里踩过一个坑直接上传原图会导致加载很慢尤其是列表页展示多只宠物头像时。我的做法是在客户端用expo-image-manipulator把图片压缩到宽度800px、质量0.7这样既保证了清晰度又把文件大小控制在200KB以内。宠物详情页的布局我改了好几版。最初想做成Tab形式健康、提醒、档案各一个Tab。后来发现用户最常看的是“当前状态”和“下一步该做什么”所以改成了卡片式布局顶部是宠物基本信息和头像中间是“当前阶段”卡片显示幼年/成年/老年下面是“即将到来的提醒”和“最近健康记录”。这样用户一打开就能看到最关键的信息。3.2 健康记录模块单表JSONB的实战应用健康记录模块是整个App最复杂的部分。前面说了用单表JSONB的方案具体实现是这样的CREATE TABLE health_records ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), pet_id UUID REFERENCES pets(id) ON DELETE CASCADE, type TEXT NOT NULL CHECK (type IN (vaccine, deworming, checkup, medication, surgery, other)), record_date DATE NOT NULL, title TEXT NOT NULL, notes TEXT, attachments JSONB DEFAULT [], details JSONB DEFAULT {}, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() );type字段区分记录类型details字段存差异化内容。比如疫苗记录的details是{vaccine_name: 狂犬疫苗, batch_number: 20240101, clinic: 某宠物医院}体检记录的details是{weight: 5.2, temperature: 38.5, heart_rate: 120}。这种设计的好处是查询极其灵活。比如要查“所有宠物的疫苗记录”直接WHERE type vaccine就行。要查“某只宠物的所有健康记录按时间倒序”直接WHERE pet_id ? ORDER BY record_date DESC。要统计“某只宠物今年打了几次疫苗”直接WHERE pet_id ? AND type vaccine AND record_date 2024-01-01。但JSONB也有坑。首先是索引问题如果你经常按details里的某个字段查询需要建GIN索引。比如我经常需要按疫苗名称搜索就建了CREATE INDEX idx_health_records_vaccine_name ON health_records USING GIN ((details-vaccine_name))。其次是数据校验问题JSONB里的字段没有类型约束全靠应用层保证。我的做法是在前端表单里做严格校验后端再用Zod做一次校验确保写入的数据格式正确。3.3 提醒与护理计划模块状态机的实现提醒模块的核心是一个状态机。每条提醒有这几个状态待处理pending、已完成completed、已跳过skipped、已过期overdue。状态之间的流转规则是pending可以变成completed或skipped如果超过截止日期还没处理自动变成overdue。护理计划模块是提醒的“模板”。比如“幼犬疫苗计划”是一个护理计划包含多条提醒第6周打第一针、第9周打第二针、第12周打第三针、第16周打狂犬疫苗。用户添加一只幼犬后可以选择应用这个护理计划系统会自动生成对应的提醒。这里有个设计难点护理计划里的时间是相对的“第6周”但提醒需要绝对时间。我的做法是在应用护理计划时根据宠物的出生日期计算出每条提醒的绝对日期。如果用户不知道宠物的确切出生日期就用估算的日期并在提醒里标注“预计时间”。提醒的推送用的是Expo Notifications。这里踩过一个坑iOS和Android的推送权限申请时机不同。iOS建议在用户第一次添加提醒时申请权限这样用户能理解为什么需要通知权限。Android 13以上也需要运行时申请。我的做法是在用户创建第一条提醒时弹出一个自定义的说明弹窗解释为什么需要通知权限然后再调用系统权限申请。这样通过率明显更高。3.4 数据同步与离线支持Supabase的实时订阅功能让多设备同步变得很简单。核心代码就几行const subscription supabase .channel(health_records) .on(postgres_changes, { event: *, schema: public, table: health_records, filter: pet_ideq.${petId} }, (payload) { // 更新本地状态 } ) .subscribe();但离线支持是个难题。Supabase的实时订阅依赖网络连接如果用户在地铁里没有信号就收不到更新。我的做法是在客户端用AsyncStorage缓存一份数据每次打开App时先从缓存加载然后再从服务器拉取最新数据。写入操作先写本地缓存再尝试同步到服务器如果失败就标记为“待同步”等网络恢复后重试。这个方案不是完美的比如多设备同时离线修改同一条记录时会有冲突。但对于一个个人用的宠物管理App来说这种冲突的概率极低我选择暂时不处理等真有用户反馈再说。4. 完整实操流程从零到可运行原型4.1 环境准备与项目初始化第一步是安装Node.js和Expo CLI。Node.js版本建议用18以上的LTS版本Expo CLI用npx create-expo-app创建项目。我选的是TypeScript模板因为宠物生命周期管理App的数据结构比较复杂类型检查能帮我避免很多低级错误。npx create-expo-app PetLifecycle --template typescript cd PetLifecycle npx expo install expo-image-picker expo-notifications expo-image-manipulator npm install supabase/supabase-js react-navigation/native react-navigation/stackSupabase项目创建很简单注册后在控制台新建一个项目拿到URL和anon key。然后在项目根目录创建.env文件EXPO_PUBLIC_SUPABASE_URLyour_project_url EXPO_PUBLIC_SUPABASE_ANON_KEYyour_anon_key这里有个安全注意事项anon key是公开的可以放在客户端。但service_role key绝对不能放在客户端那个key有管理员权限。所有需要管理员权限的操作都应该通过Supabase的边缘函数来做。4.2 数据库表结构与RLS策略配置在Supabase的SQL Editor里执行建表语句。除了前面说的health_records表还需要创建users、pets、reminders、care_plans这几张表。这里重点说RLS行级安全策略因为这是Supabase安全模型的核心。-- 启用RLS ALTER TABLE pets ENABLE ROW LEVEL SECURITY; -- 用户只能看到自己的宠物 CREATE POLICY Users can view own pets ON pets FOR SELECT USING (auth.uid() user_id); -- 用户只能插入自己的宠物 CREATE POLICY Users can insert own pets ON pets FOR INSERT WITH CHECK (auth.uid() user_id); -- 用户只能更新自己的宠物 CREATE POLICY Users can update own pets ON pets FOR UPDATE USING (auth.uid() user_id); -- 用户只能删除自己的宠物 CREATE POLICY Users can delete own pets ON pets FOR DELETE USING (auth.uid() user_id);health_records表的RLS策略稍微复杂一点因为需要检查记录所属的宠物是否属于当前用户CREATE POLICY Users can view own health records ON health_records FOR SELECT USING ( EXISTS ( SELECT 1 FROM pets WHERE pets.id health_records.pet_id AND pets.user_id auth.uid() ) );这个策略的性能需要注意。如果health_records表数据量很大每次查询都要做一次子查询可能会慢。优化方法是在health_records表里冗余一个user_id字段直接比较auth.uid() user_id避免子查询。我一开始没加这个字段后来数据量上来了才发现问题加字段后查询速度快了很多。4.3 核心页面与组件的AI生成过程用Claude Code生成页面组件时我的提示词策略是“先描述页面结构再描述交互逻辑最后描述数据来源”。比如生成宠物详情页时我是这么说的“创建一个React Native的宠物详情页组件。页面顶部显示宠物头像圆形80x80、名字大号字体、品种和年龄小号灰色字体。中间是一个卡片显示当前生命周期阶段幼年/成年/老年根据宠物年龄自动计算。下面是一个列表显示最近5条健康记录每条记录显示类型图标、标题、日期。最下面是一个悬浮按钮点击后跳转到添加健康记录页面。数据从Supabase的pets表和health_records表获取使用useEffect在组件挂载时加载。”Claude Code生成的代码基本可用但有几个地方需要手动调整。一是加载状态的处理它默认没有加loading指示器需要自己补。二是错误处理它只写了happy path网络请求失败的情况需要自己加try-catch。三是样式细节它生成的样式比较基础需要根据设计稿调整间距、圆角、阴影等。我的经验是把AI生成的代码当作“初稿”不要指望一次完美。先让它把结构搭出来然后自己逐行审查补充边界情况处理。这样比从零开始写快很多但比直接复制粘贴慢——不过后者往往会在后期付出更大的调试成本。4.4 提醒推送的配置与测试Expo Notifications的配置分几步。首先在app.json里添加插件配置{ expo: { plugins: [ [ expo-notifications, { icon: ./assets/notification-icon.png, color: #ffffff } ] ] } }然后创建一个通知服务模块封装权限申请、通知调度、通知取消等操作。这里有个关键点本地通知和远程推送是两回事。本地通知是App自己调度的不需要服务器参与适合提醒场景。远程推送需要服务器通过Expo的推送服务发送适合社交互动场景。宠物管理App主要用本地通知就够了。测试本地通知时模拟器上可能无法正常触发建议用真机测试。另外iOS的通知权限申请只能弹一次如果用户拒绝了后续只能引导用户去设置里手动开启。所以第一次申请权限的时机很重要我的做法是在用户创建第一条提醒时先弹一个自定义弹窗解释为什么需要通知权限用户点击“好的”后再调用系统权限申请。5. 常见问题与排查技巧实录5.1 Supabase连接与认证问题排查问题一App启动后一直卡在加载状态。排查思路先检查.env文件里的URL和key是否正确然后检查Supabase项目是否处于活跃状态免费项目一段时间不活动会被暂停。如果都没问题在App里加一个简单的测试查询比如supabase.from(pets).select(count)看是否能返回数据。问题二用户登录后看不到自己的数据。这通常是RLS策略配置错误。排查方法在Supabase控制台的SQL Editor里用SET request.jwt.claim.sub user_id模拟用户身份然后执行查询看是否能返回数据。如果返回空说明RLS策略有问题。问题三实时订阅不工作。检查几点是否在Supabase控制台启用了Realtime功能默认是关闭的、订阅的channel名称是否唯一、filter条件是否正确。还有一个容易忽略的点如果表启用了RLS实时订阅也会受RLS策略影响确保策略允许当前用户读取数据。5.2 数据模型相关的典型问题问题一JSONB字段查询性能差。前面说过如果经常按JSONB里的某个字段查询一定要建GIN索引。另外如果某个字段的查询频率特别高考虑把它从JSONB里提出来变成独立的列。问题二日期时区问题。Supabase的timestamptz类型存储的是UTC时间但用户看到的是本地时间。我的做法是在客户端统一用dayjs处理日期显示时转换为本地时区存储时转换为UTC。这里有个坑如果用户跨时区使用提醒的触发时间可能会偏移。解决方案是在创建提醒时把用户的时区信息也存下来调度通知时根据时区计算正确的触发时间。问题三软删除与硬删除的取舍。宠物档案和健康记录我用了硬删除ON DELETE CASCADE因为用户删除宠物时通常希望彻底清除数据。但提醒记录我用了软删除加了一个deleted_at字段因为提醒的历史记录对分析用户行为有价值。这个取舍没有绝对的对错取决于业务需求。5.3 AI辅助开发的常见陷阱陷阱一AI生成的代码缺少错误处理。这是最普遍的问题。AI倾向于写happy path代码网络请求失败、数据为空、权限被拒绝这些情况它往往不考虑。我的做法是每次让AI生成代码后自己过一遍把所有可能失败的地方加上try-catch和fallback UI。陷阱二AI会“幻觉”出不存在的API。比如它可能会调用一个Supabase SDK里不存在的方法或者参数格式不对。解决方法遇到不确定的API调用去官方文档确认一下。Claude Code的好处是你可以直接问它“这个API在最新版本里还存在吗”它通常会承认错误并给出正确的写法。陷阱三AI生成的样式代码不够精细。它给的样式往往是“能用但不好看”。我的做法是把设计稿的关键参数间距、字号、颜色值明确告诉它比如“卡片内边距16px圆角12px阴影offset 0 2px blur 8px color rgba(0,0,0,0.1)”。这样生成的样式更接近设计稿。陷阱四上下文丢失。对话太长时AI可能会忘记之前的设计决策。我的做法是定期把关键决策整理成一个文档在开始新对话时先把这个文档发给它。另外Claude Code支持在项目里放一个CLAUDE.md文件把项目约定写进去它会自动读取。5.4 性能优化与上线前的检查清单上线前我做了一轮性能优化主要做了这几件事图片懒加载宠物列表页的头像用expo-image的lazy属性滚动到可视区域才加载。查询优化给所有外键字段加了索引给常用的查询条件加了复合索引。缓存策略用React Query管理服务端状态设置合理的staleTime和cacheTime减少不必要的网络请求。包体积优化用expo-optimize压缩图片资源移除未使用的依赖。上线前的检查清单检查项状态备注RLS策略覆盖所有表已完成每张表都启用了RLS错误边界处理已完成每个页面都有ErrorBoundary加载状态已完成所有异步操作都有loading指示空状态已完成无数据时有引导文案通知权限引导已完成自定义弹窗系统申请离线缓存已完成AsyncStorage缓存同步队列深色模式未完成优先级低后续迭代多语言未完成优先级低后续迭代6. 这个项目后续还能怎么扩展宠物生命周期管理App的基础功能做完后我梳理了几个扩展方向。第一个是社交功能让用户可以把宠物的健康记录分享给家人或宠物医生。这个功能的技术难点在于权限控制——分享给谁、分享哪些内容、是否可以撤销。Supabase的RLS可以支持这种细粒度权限但需要设计一套分享令牌机制。第二个是数据分析根据用户录入的健康记录生成宠物的健康趋势报告。比如体重变化曲线、疫苗覆盖率、体检异常项统计。这个功能需要在前端做数据聚合和可视化可以用Victory Native或者Recharts。第三个是AI健康助手根据宠物的品种、年龄、健康记录给出个性化的护理建议。这个功能需要调用大语言模型的API把宠物的结构化数据转换成提示词然后解析模型的返回结果。这里要注意的是AI给出的建议只能作为参考不能替代专业兽医的诊断。第四个是宠物用品商城根据宠物的生命周期阶段推荐合适的食品、玩具、护理用品。这个功能涉及电商逻辑复杂度较高适合作为独立的商业化探索。我个人在实际操作中的体会是用AI辅助开发最大的价值不是“省时间”而是“降低启动门槛”。以前你有一个想法可能要花几周才能搭出一个能看的原型现在可能几天就能搞定。但省下来的时间不应该用来摸鱼而应该用来思考产品本身——用户到底需要什么、这个功能真的有用吗、有没有更简单的实现方式。AI帮你解决了“怎么做”的问题但“做什么”和“为什么做”永远是你自己的事。最后再分享一个小技巧用Claude Code开发时养成“小步提交”的习惯。每完成一个可运行的功能点就git commit一次这样如果AI在后续对话中改坏了代码你可以随时回滚到上一个可用状态。我踩过几次坑有一次让AI重构一个模块结果它把之前能用的功能改出了bug因为没有及时提交花了不少时间才恢复。从那以后我就养成了频繁提交的习惯虽然commit记录看起来有点碎但安全感强很多。
阅读完成 · 觉得有帮助?
咨询建站