news 2026/11/2 1:11:17

AI全流程开发实战:从零搭建宠物生命周期管理App

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
AI全流程开发实战:从零搭建宠物生命周期管理App

1. 为什么我选择用AI全流程搭建一个宠物生命周期管理App

先说结论:这个项目从空目录到可运行的原型,我全程没有手写超过200行代码,绝大部分逻辑、组件、数据模型甚至测试用例都是通过对话式AI编程工具生成的。整个过程大概花了两个周末的碎片时间,最终跑通了一个能记录宠物从领养、疫苗、驱虫、体检到离世全过程的移动端应用。

宠物生命周期管理这个需求,其实是我身边好几个养宠朋友的真实痛点。他们手机里散落着各种备忘录、相册、微信收藏,疫苗本拍完就忘,驱虫时间靠脑补,体检报告找不到,等到宠物生病了才手忙脚乱翻记录。市面上虽然有一些宠物管理App,但要么功能太重,要么数据不在自己手里,要么就是广告满天飞。所以我想做一个轻量的、数据本地优先的、能覆盖宠物完整生命周期的工具。

这个项目适合谁看?如果你是有一定编程基础但没时间从零写样板代码的开发者,或者你想了解AI编程工具在真实项目里到底能扛多少活,再或者你自己养宠、想动手做一个趁手的小工具,那这篇内容应该能给你不少参考。我会把整个搭建流程、关键决策、踩过的坑、以及AI在哪些环节特别靠谱、哪些环节必须人工兜底,全部摊开来讲。

核心关键词我先埋在这里:Claude Code、AI全流程开发、宠物生命周期管理、从零搭建、移动端应用。后面所有内容都围绕这几个词展开,不跑题。

2. 项目整体设计与技术选型思路

2.1 需求拆解:宠物生命周期到底要管什么

我先把“宠物生命周期”这个模糊概念拆成了可落地的功能模块。一只宠物从进入家庭到离开,核心事件节点其实就那么几类:

  • 基础档案:品种、性别、生日、领养日期、绝育状态、芯片号、体重曲线
  • 健康记录:疫苗(种类、批次、接种日期、下次到期)、驱虫(体内/体外、药品名、日期)、体检(医院、项目、报告图片)、过敏史、慢性病
  • 日常事件:喂食记录、体重测量、洗澡美容、行为异常备注
  • 提醒系统:疫苗到期提醒、驱虫周期提醒、体检建议提醒、用药提醒
  • 纪念模块:照片时间线、重要时刻标记、离世纪念页

这五块里,提醒系统是刚需中的刚需,也是我朋友们抱怨最多的点。所以第一版我把重心放在档案+健康记录+提醒这三块,纪念模块做了个最简版。

2.2 技术栈选择:为什么是这套组合

技术选型我纠结了大概半天,最后定下来的是:

层级选型理由
客户端React Native + Expo跨平台,Expo能省掉大量原生配置,AI生成代码后能直接扫码预览
状态管理Zustand比Redux轻太多,AI生成不容易出错,样板代码少
本地存储SQLite (expo-sqlite)数据本地优先,结构化查询方便,离线可用
导航Expo Router文件路由,AI对文件结构的理解比配置式路由更准
UI组件自建 + 少量社区库避免重型UI库,减少AI生成时的版本冲突
提醒expo-notifications本地通知,不依赖服务端

这里重点说两个决策逻辑。第一,为什么不用Flutter?因为我实测下来,Claude Code对React Native生态的代码生成准确率明显更高,尤其是Expo相关的API,它给出的代码基本能直接跑,Flutter的Dart代码偶尔会出现包版本对不上的情况。第二,为什么坚持本地优先?宠物数据涉及隐私,而且用户不一定随时有网,本地SQLite是最稳妥的方案,后续要同步再说。

2.3 AI全流程开发的边界在哪里

在动手之前,我给AI编程工具划了个能力边界,这个边界是我踩过坑之后总结的:

AI特别擅长的:生成CRUD代码、写SQL建表语句、生成表单组件、写工具函数、生成测试用例、解释报错、重构重复代码、生成类型定义。

AI需要人工兜底的:涉及具体业务规则的判断(比如驱虫周期到底怎么算)、UI的最终视觉调整、第三方服务的真实配置、数据迁移逻辑、性能瓶颈的定位。

AI基本帮不上忙的:产品决策、用户真实反馈的解读、上架审核相关的合规问题。

把这个边界想清楚之后,整个开发流程就顺了——我把80%的样板工作交给AI,自己专注在业务规则和最终验收上。

3. 从零搭建的核心环节与实操要点

3.1 项目初始化:让AI生成可运行骨架

第一步是创建Expo项目。我没有手动敲命令,而是直接让Claude Code帮我生成初始化命令和目录结构。我给的指令大概是这样的:

帮我创建一个基于Expo的React Native项目,使用TypeScript, 路由用Expo Router,状态管理用Zustand,本地数据库用expo-sqlite。 项目名暂定PetLife,需要包含基础的目录结构: app/ 放路由页面,components/ 放通用组件, db/ 放数据库相关,stores/ 放状态管理, utils/ 放工具函数,types/ 放类型定义。

它返回的命令是:

npx create-expo-app@latest PetLife --template blank-typescript cd PetLife npx expo install expo-router expo-sqlite expo-notifications zustand

然后它自动帮我改了package.json的入口,创建了app/_layout.tsx和app/index.tsx。这里有个细节要注意:Expo Router的版本和Expo SDK版本强相关,AI有时候会给出不匹配的版本号。我的做法是先用npx expo install让Expo自己解析兼容版本,而不是直接npm install。

注意:每次让AI生成依赖安装命令后,优先用npx expo install而不是npm install,前者会自动选择与当前SDK兼容的版本,能避免大量“包版本冲突”的报错。

初始化完成后,我跑了一次npx expo start,确认能正常启动,再进入下一步。这个“先跑通再开发”的习惯非常重要,不然后面出了问题你分不清是初始化的问题还是业务代码的问题。

3.2 数据模型设计:AI辅助建表与类型定义

数据模型是整个App的地基。我先把需求用自然语言描述给AI,让它生成SQLite建表语句和对应的TypeScript类型。

我给的描述是:需要pet表存宠物基础信息,health_record表存健康记录(疫苗、驱虫、体检用type字段区分),reminder表存提醒,weight_log表存体重记录,photo表存照片关联。

AI生成的建表语句核心部分:

CREATE TABLE IF NOT EXISTS pet ( id TEXT PRIMARY KEY, name TEXT NOT NULL, species TEXT NOT NULL, breed TEXT, gender TEXT, birthday TEXT, adopt_date TEXT, neutered INTEGER DEFAULT 0, chip_no TEXT, avatar TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS health_record ( id TEXT PRIMARY KEY, pet_id TEXT NOT NULL, type TEXT NOT NULL, title TEXT, record_date TEXT NOT NULL, next_date TEXT, hospital TEXT, notes TEXT, FOREIGN KEY (pet_id) REFERENCES pet(id) ON DELETE CASCADE );

这里我做了个关键调整:AI一开始把疫苗、驱虫、体检分成了三张表,我改成了统一用health_record加type字段。理由是这三类记录的结构高度相似(都有日期、下次日期、机构、备注),分表会导致查询和UI组件重复度极高。合并之后,一个列表组件就能渲染所有健康记录,维护成本大幅下降。

对应的TypeScript类型AI也一并生成了,我让它把type字段做成联合类型:

export type HealthRecordType = 'vaccine' | 'deworm' | 'checkup' | 'medication'; export interface HealthRecord { id: string; petId: string; type: HealthRecordType; title: string; recordDate: string; nextDate?: string; hospital?: string; notes?: string; }

实操心得:让AI生成类型定义时,明确要求它使用联合类型而不是string,这样在写业务代码时编辑器能给出准确的自动补全和类型检查,能提前拦掉一大批拼写错误。

3.3 数据库封装层:一次生成,多处复用

SQLite的原生API用起来比较啰嗦,我让AI帮我封装了一层通用的数据库操作。我给的指令是:封装一个db工具,提供initDB、insert、update、delete、queryAll、queryOne这几个方法,支持泛型,自动处理JSON字段的序列化。

AI生成的封装大概长这样:

import * as SQLite from 'expo-sqlite'; const db = SQLite.openDatabaseSync('petlife.db'); export function initDB() { db.execSync(`CREATE TABLE IF NOT EXISTS pet (...)`); db.execSync(`CREATE TABLE IF NOT EXISTS health_record (...)`); // 其他表 } export function queryAll<T>(sql: string, params: any[] = []): T[] { return db.getAllSync<T>(sql, params); } export function insert(table: string, data: Record<string, any>) { const keys = Object.keys(data); const placeholders = keys.map(() => '?').join(','); const sql = `INSERT INTO ${table} (${keys.join(',')}) VALUES (${placeholders})`; return db.runSync(sql, Object.values(data)); }

这个封装层是整个项目里复用率最高的代码,后面所有业务模块都基于它。我在这里加了一个AI没想到的细节:对undefined值的处理。原生SQLite绑定参数时如果传undefined会报错,所以我在insert和update里加了一层过滤,把undefined转成null。这个坑我是实际跑的时候才发现的,AI生成的初版没考虑这个边界。

3.4 提醒系统:业务规则必须人工定义

提醒系统是AI帮不上太多忙的地方,因为“疫苗什么时候该打”“驱虫周期多久”这些是业务规则,不是代码问题。我的做法是自己先把规则定清楚,再让AI实现。

我定的规则是这样的:

  • 疫苗:记录时填写next_date,到期前7天、3天、当天各提醒一次
  • 驱虫:体内每3个月,体外每月,根据上次记录日期自动推算下次日期
  • 体检:每年一次,根据上次体检日期推算
  • 用药:按用户填写的具体时间提醒

规则定好后,我让AI生成提醒调度逻辑。核心思路是:每次App启动时,扫描所有health_record,计算未来30天内到期的提醒,用expo-notifications注册本地通知。

export async function scheduleReminders() { const records = queryAll<HealthRecord>('SELECT * FROM health_record WHERE next_date IS NOT NULL'); const now = new Date(); const thirtyDaysLater = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000); for (const record of records) { const nextDate = new Date(record.nextDate!); if (nextDate > now && nextDate < thirtyDaysLater) { await Notifications.scheduleNotificationAsync({ content: { title: `${record.title} 即将到期`, body: `记得安排时间` }, trigger: { date: nextDate }, }); } } }

注意:本地通知在iOS和Android上的权限申请时机不一样,Android 13以上需要动态申请POST_NOTIFICATIONS权限。这个AI一开始漏了,我是在真机测试时发现通知不弹才补上的。所以涉及系统权限的功能,一定要在真机上验证,模拟器经常表现不一致。

3.5 UI组件生成:从描述到可用界面

UI部分我采用了“先描述布局,再让AI生成,最后自己微调”的流程。以宠物详情页为例,我的描述是:顶部是宠物头像和名字,下面是一排快捷操作按钮(记录疫苗、记录驱虫、记录体重),再下面是时间线形式展示所有健康记录,按日期倒序。

AI生成的组件结构基本可用,但视觉上比较朴素。我做了几处调整:把时间线的圆点改成带类型图标的样式,给不同记录类型配了不同的颜色标识,列表项加了左滑删除。这些调整我没有全部让AI做,因为视觉细节反复对话的效率不如自己直接改。

这里有个提效技巧:让AI生成组件时,要求它把样式和逻辑分离,样式用StyleSheet.create单独放在文件底部。这样我调整视觉时只需要改样式部分,不会碰到逻辑代码。

4. 完整实操流程与关键环节实现

4.1 环境准备与项目启动的完整命令序列

把前面零散的步骤串起来,从零到能跑,完整的命令序列是这样的:

# 1. 创建项目 npx create-expo-app@latest PetLife --template blank-typescript cd PetLife # 2. 安装核心依赖(用expo install保证版本兼容) npx expo install expo-router expo-sqlite expo-notifications expo-image-picker npx expo install zustand # 3. 配置Expo Router入口(修改package.json的main字段) # "main": "expo-router/entry" # 4. 启动开发服务器 npx expo start

启动后用手机上的Expo Go扫码就能预览。这里提醒一句:Expo Go对某些原生模块支持有限,如果后面要加蓝牙、后台定位这类功能,需要切换到development build。宠物管理App第一版用不到这些,Expo Go足够。

4.2 数据库初始化与迁移的实操记录

数据库初始化我放在app/_layout.tsx的根布局里,确保App启动时只执行一次:

useEffect(() => { initDB(); scheduleReminders(); }, []);

关于迁移,第一版我没做复杂的版本管理,只在initDB里用了CREATE TABLE IF NOT EXISTS。但这里有个隐患:如果后面要加字段,IF NOT EXISTS不会帮你改表结构。我的做法是加了一个简单的版本号机制:

const DB_VERSION = 1; export function initDB() { db.execSync('CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT)'); const row = db.getFirstSync<{ value: string }>('SELECT value FROM meta WHERE key = ?', ['db_version']); const currentVersion = row ? parseInt(row.value) : 0; if (currentVersion < 1) { // 建表语句 db.execSync('INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)', ['db_version', '1']); } // 后续版本在这里加 if (currentVersion < 2) { ... } }

这个机制很简单但够用,后面加字段时只需要递增版本号,写对应的ALTER TABLE语句就行。AI一开始没主动提这个,是我要求它“考虑后续表结构变更”之后才补上的。

4.3 核心页面实现:宠物列表与详情页

宠物列表页的逻辑是:从数据库查所有宠物,渲染成卡片列表,点击进入详情。AI生成的初版代码里,数据获取直接写在组件的useEffect里,我改成了用Zustand的store统一管理:

// stores/petStore.ts import { create } from 'zustand'; import { queryAll } from '../db'; interface PetStore { pets: Pet[]; loadPets: () => void; addPet: (pet: Pet) => void; } export const usePetStore = create<PetStore>((set) => ({ pets: [], loadPets: () => { const pets = queryAll<Pet>('SELECT * FROM pet ORDER BY created_at DESC'); set({ pets }); }, addPet: (pet) => { insert('pet', pet); set((state) => ({ pets: [pet, ...state.pets] })); }, }));

这样改的好处是,列表页、详情页、提醒模块都能共享同一份宠物数据,任何一处更新,其他页面自动刷新。AI生成的初版是每个页面各自查库,数据不同步的问题很烦人。

详情页的时间线组件是UI里最复杂的一块。我让AI生成时特别强调了“按日期倒序、不同类型显示不同图标和颜色、支持左滑删除”。它生成的代码里,左滑删除用了react-native-gesture-handler,这个库Expo默认就带,不用额外装。

4.4 提醒调度的参数计算与实测

提醒这块我实测了好几轮,把参数计算逻辑理清楚了。以驱虫为例:

  • 体外驱虫:周期30天,下次日期 = 上次日期 + 30天
  • 体内驱虫:周期90天,下次日期 = 上次日期 + 90天
  • 疫苗:周期365天(狂犬),下次日期 = 上次日期 + 365天

这些周期我做成常量放在utils/constants.ts里,方便后续调整:

export const CYCLE_DAYS = { deworm_external: 30, deworm_internal: 90, vaccine_rabies: 365, vaccine_combined: 365, checkup: 365, };

提醒触发时机我实测下来,提前7天和提前1天各提醒一次比较合理。提前太多用户会忽略,提前太少来不及安排。通知内容里带上宠物名字和记录类型,比如“豆豆的体外驱虫还有3天到期”,比干巴巴的“驱虫提醒”点击率高很多。

实操心得:本地通知的trigger如果传具体日期,在iOS上如果这个日期已经过了,通知不会触发也不会报错,静默失败。所以调度前一定要判断日期是否在未来,这个判断AI经常漏,需要自己补。

5. 常见问题与排查技巧实录

5.1 AI生成代码的典型问题速查表

在整個开发过程中,我整理了一份AI生成代码的高频问题清单,这些问题几乎每次都会遇到,提前知道能省大量调试时间:

问题现象根本原因解决方式
包版本冲突,启动报错AI给的版本号与Expo SDK不匹配改用npx expo install重新安装
SQLite绑定undefined报错参数里有undefined值封装层统一转null
通知不触发权限未申请或日期已过检查权限+判断日期未来
列表数据不刷新各页面独立查库用Zustand统一状态
类型报错但运行正常AI用了any或类型不完整补全联合类型定义
路由跳转白屏Expo Router文件命名不规范检查app目录下文件结构

5.2 数据库操作的避坑要点

SQLite在React Native里是同步API,这意味着如果一次查大量数据,会阻塞UI线程。宠物管理App的数据量不大,一般不会遇到性能问题,但有两个点要注意:

第一,批量插入时不要循环调用insert,每次调用都是一次独立事务,很慢。应该用事务包起来:

db.withTransactionSync(() => { for (const record of records) { insert('health_record', record); } });

第二,查询时尽量只查需要的字段,不要SELECT *。虽然数据量小的时候差别不大,但养成习惯没坏处。AI生成的代码默认都是SELECT *,我一般会手动改成具体字段。

5.3 真机测试与模拟器的差异记录

这个必须单独说,因为我在这上面浪费了不少时间。模拟器和真机的差异主要体现在:

  • 通知:模拟器上通知行为不稳定,真机才准
  • 图片选择:expo-image-picker在模拟器上可能没有相册权限,真机正常
  • 性能:模拟器跑得动不代表真机流畅,尤其是长列表
  • 字体渲染:模拟器和真机的字体度量有细微差异,可能导致布局错位

我的建议是,核心功能每完成一块就在真机上验证一次,不要等全部做完再测。真机测试用Expo Go扫码就行,改代码后自动热更新,效率很高。

5.4 数据备份与导出的实现思路

宠物数据对用户来说很珍贵,万一换手机或者误删就麻烦了。我加了一个简单的导出功能:把所有数据序列化成JSON,通过系统分享面板导出。

export async function exportData() { const pets = queryAll('SELECT * FROM pet'); const records = queryAll('SELECT * FROM health_record'); const data = { pets, records, exportAt: new Date().toISOString() }; const json = JSON.stringify(data, null, 2); await Sharing.shareAsync(`data:text/json;base64,${btoa(json)}`); }

导入功能我暂时没做,因为涉及数据合并和冲突处理,逻辑比较复杂。第一版先保证能导出,用户至少有个备份。这个功能AI生成得很快,但btoa对中文支持有问题,需要先做编码转换,这个坑我踩过。

6. 我对AI全流程开发这件事的真实体会

两个周末做下来,最大的感受是:AI编程工具确实能把开发效率提升一个量级,但它提升的是“实现效率”,不是“决策效率”。产品要做什么、业务规则怎么定、数据模型怎么设计,这些还是得自己想清楚。AI更像一个执行力极强但需要明确指令的初级开发者,你给它的描述越精确,它返回的代码越可用。

另一个体会是,用AI写代码,调试能力反而更重要了。因为代码不是你一行行敲的,出了问题你得快速定位是哪一层的问题。我的习惯是每生成一块代码就先跑通再往下走,绝不攒着一堆代码一起调,那样排查起来是灾难。

这个宠物生命周期管理App目前还是个原型,但核心流程已经跑通了。后续我打算加数据同步和多人共享(比如家庭成员共同管理一只宠物),这两个功能涉及服务端,AI能帮的比例会下降,但基础的接口定义和客户端逻辑还是可以交给它。如果你也在做类似的小工具,我的建议是先从最痛的那个点做起,别一上来就追求大而全,跑通一个闭环比什么都重要。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/20 14:41:23

强化学习中的基于模型方法:Dyna与MCTS深度解析

强化学习笔记写到第七篇&#xff0c;我终于开始啃基于模型的RL&#xff08;Model-Based RL&#xff09;这条线了。在这条线上有两个绕不开的名字&#xff1a;Dyna算法和MCTS&#xff08;蒙特卡洛树搜索&#xff09;。要说清楚它们&#xff0c;得先从无模型方法的一个别扭之处说…

作者头像 李华
网站建设 2026/11/1 7:58:47

SpringBoot2+Vue3+MyBatis-Plus工资管理系统全栈项目解析

做 Java Web 项目的朋友应该都有同感&#xff1a;框架学完了、视频看完了&#xff0c;手头却缺一个能把整套技术串起来、结构又足够规范的项目。工资信息管理系统恰好是这类项目中非常典型的一个——业务贴近真实场景&#xff0c;技术面上覆盖了后端接口、权限控制、前端页面、…

作者头像 李华
网站建设 2026/10/25 2:10:43

C++模板跨编译器兼容性实战:从差异分类到架构设计

做跨平台基础库的人&#xff0c;多半都被模板代码的兼容性问题折磨过。同一套头文件&#xff0c;在 GCC 下面编译得干干净净&#xff0c;换到 Clang 突然给你抛出一堆dependent name is not a type的报错&#xff1b;在 MSVC 上顺利实例化的模板&#xff0c;拿到 GCC 却又告诉你…

作者头像 李华
网站建设 2026/10/29 11:26:55

TFLite移动端集成实战:从模型导出到跨平台稳定推理

1. 为什么“集成”比“模型训练”更难啃——从一个被忽略的部署断层说起很多人以为&#xff0c;TensorFlow Lite&#xff08;TFLite&#xff09;只是TensorFlow的“轻量版”&#xff0c;把训练好的模型导出一下、跑在手机上就完事了。我去年在某跨平台图像处理Demo中也这么想&a…

作者头像 李华
网站建设 2026/10/18 21:12:32

大模型训练三重逻辑:递推、递归与三明治的底层原理

1. 从“调包侠”到“炼丹师”的分水岭在哪里如果你现在打开任何一个技术社区&#xff0c;搜“大模型训练”&#xff0c;跳出来的结果大概率是两类&#xff1a;一类是教你pip install某个库然后三行代码跑通推理&#xff0c;另一类是甩给你一篇论文链接让你自己啃。前者让你觉得…

作者头像 李华
网站建设 2026/10/26 19:38:17

CLM5.0陆面模型核心模块解析与工程实践指南

1. 项目概述CLM&#xff08;Community Land Model&#xff09;作为地球系统模型中的重要陆面过程模块&#xff0c;其模拟能力直接影响着气候变化研究的精度。记得2015年参与青藏高原冻土研究时&#xff0c;我们团队花了整整三个月才摸清CLM5.0中碳氮循环与冻土水热的耦合机制。…

作者头像 李华