在这里插入图片描述
在这里插入图片描述

实例:宠物档案(Pet Profile)|技术:全量代码解读、操作剧本、1主N从+时间序列模式抽象

一、本篇定位

前两篇分别讲了数据层与 UI 层,本篇完成三件事:

  1. 全量源码逐段解读——PetDao 与 PetPage 从头到尾过一遍;
  2. 运行效果演示——从建档到疫苗提醒的完整操作剧本;
  3. 模式抽象——"1 主 N 从 + 时间序列"通用模板,附两个改写场景。

二、完整源码:PetDao.ets

/**
 * 宠物档案数据访问对象(DAO)
 * 对应文章:32_宠物档案/32-1 建表与数据层设计、32-2 页面UI与操作实现、32-3 完整代码与运行效果
 * 核心能力:宠物主表 + 喂食/体重记录从表(时间序列)、疫苗到期提醒、体重趋势数据
 */
import { relationalStore } from '@kit.ArkData';
import { common } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';

/** 宠物实体,对应 pet 表结构 */
export interface Pet {
  id: number;
  name: string;
  species: string;
  breed: string;
  gender: number;       // 0 男 / 1 女
  birthday: number;
  avatar: string;
  nextVaccine: number;  // 下次疫苗日期(0=无)
  note: string;
  createdAt: number;
}

/** 体重记录实体,对应 weight_record 表结构 */
export interface WeightRecord {
  id: number;
  petId: number;
  weight: number;
  createdAt: number;
}

/** 喂食记录实体,对应 feed_record 表结构 */
export interface FeedRecord {
  id: number;
  petId: number;
  food: string;
  amount: string;
  feedTime: number;
}

/** 宠物视图:宠物 + 最新体重 + 疫苗状态 */
export interface PetView {
  pet: Pet;
  latestWeight: number;
  weightCount: number;
  vaccineDueDays: number;
  feedCount: number;    // 今日喂食次数
}

const DOMAIN = 0x0001;
const TAG = 'PetDao';

/** 疫苗提醒阈值(天) */
export const VACCINE_WARN_DAYS = 30;

export class PetDao {
  private static readonly TABLE = 'pet';
  private static readonly WEIGHT_TABLE = 'weight_record';
  private static readonly FEED_TABLE = 'feed_record';
  private static store?: relationalStore.RdbStore;
  // getStore / 行映射 / CRUD / 记录 / 统计 / 种子数据(见上篇逐段解读)
}

逐段解读总表

代码段 职责 关键决策
实体接口 Pet / WeightRecord / FeedRecord / PetView PetView 封装 4 派生数据
VACCINE_WARN_DAYS 疫苗阈值常量 徽标与筛选同步
getStore 单例 + 建 3 表 + 3 索引 1 主 2 从
insertPet / updatePet 建档与更新 updatePet 支持疫苗设置
deletePet 循环删两从表再删主表 无孤儿
addWeight / addFeed 时间序列追加 只增不改
queryWeights / queryFeeds 记录倒序查询 详情流数据
latestWeight 最新值查询 倒序+limit 范式
todayFeedCount 今日统计 0 点边界
queryPetViews 视图组装 4 派生数据
vaccineDue / summary 临期筛选 + 统计 reduce 聚合
initSeedData 2 宠物双记录 疫苗/体重演示

三、完整源码:PetPage.ets

/**
 * 宠物档案页面
 * 对应文章:32_宠物档案/32-1 ~ 32-3
 * 功能:宠物卡片列表 + 新增宠物 + 详情(体重记录/喂食记录/疫苗提醒)+ 记录体重/喂食
 */
import { common } from '@kit.AbilityKit';
import { promptAction } from '@kit.ArkUI';
import { PetDao, Pet, PetView, WeightRecord, FeedRecord, VACCINE_WARN_DAYS } from '../../database/PetDao';

@Entry
@Component
struct PetPage {
  @State views: PetView[] = [];
  @State summaryText: string = '';
  @State formVisible: boolean = false;
  @State fName: string = '';
  @State fSpecies: string = '';
  @State fBreed: string = '';
  @State detailVisible: boolean = false;
  @State current: PetView | null = null;
  @State weights: WeightRecord[] = [];
  @State feeds: FeedRecord[] = [];
  @State wFormVisible: boolean = false;
  @State fWeight: string = '';
  @State feedFormVisible: boolean = false;
  @State fFood: string = '';
  @State fAmount: string = '';
  @State vaccineFormVisible: boolean = false;
  @State fVaccine: string = '';
  private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;

  aboutToAppear(): void {
    this.refresh();
  }
  // refresh/onAddPet/openDetail/onAddWeight/onAddFeed/onSaveVaccine/onDelete/vaccineInfo/fmtDate/fmtTime 见中篇
}

页面组织纪律回顾

  1. 状态量五组:列表态、建档表单(f*)、详情态(current/weights/feeds)、体重表单、喂食表单、疫苗表单——6 组 @State 清晰分区;
  2. 5 个弹层互斥:form/detail/wForm/feedForm/vaccineForm;
  3. UI 零 SQL:疫苗徽标、今日喂食全部来自 PetView。

四、接入工程三步

Step 1:放文件

  • PetDao.etsentry/src/main/ets/database/
  • PetPage.etsentry/src/main/ets/pages/samples/

Step 2:注册路由(main_pages.json)

"pages/samples/PetPage",

Step 3:首页入口(Index.ets)

Button('🐾 32 宠物档案')
  .fontSize(14)
  .width('80%')
  .onClick(() => {
    this.getUIContext().getRouter().pushUrl({ url: 'pages/samples/PetPage' });
  })

五、运行效果演示(操作剧本)

场景一:首次打开

步骤 界面表现 数据来源
1 标题栏:“🐾 宠物档案”,统计:“宠物 2 只 · 今日已喂 3 次 · 疫苗临期 1 只” summary
2 列表 2 只:🐈 咪咪(中华田园猫 ♂,橙徽标"疫苗临期 25 天")、🐕 旺财(金毛 ♀,灰徽标"疫苗未设") queryPetViews
3 咪咪卡片:“体重 4.6 kg(4 次)· 今日已喂 2 次”;旺财:“体重 19.0 kg(2 次)· 今日已喂 1 次” latestWeight/todayFeedCount

场景二:记录体重

  1. 点咪咪 → 详情抽屉:体重记录 4 条(4.2 → 4.4 → 4.5 → 4.6 kg,上升趋势);
  2. 点"⚖️ 记体重"→ 输入 4.7 → 保存 → toast"✅ 体重已记录";
  3. 抽屉体重列表顶部出现"4.7 kg",卡片摘要更新"4.7 kg(5 次)"。

场景三:记录喂食

  1. 抽屉点"🍖 记喂食"→ 食物"猫粮"、分量"30克"→ 记录 → toast"🍖 已记录喂食";
  2. 喂食记录列表顶部新增,标题栏统计"今日已喂 3 次"。

场景四:疫苗设置与提醒

  1. 抽屉点"设置疫苗日期"→ 输入 2026-07-01 → 保存 → toast"✅ 疫苗日期已更新";
  2. 徽标按剩余天数变化:>30 天绿色"疫苗正常"、≤30 橙色"疫苗临期 X 天"、已过红色"疫苗已到期 X 天";
  3. 留空保存 → 清除疫苗日期,徽标变灰"疫苗未设"。

场景五:新增宠物

点 + → 名"豆豆"、种类"仓鼠"、品种"三线"→ 建档 → toast"✅ 宠物已建档",列表出现 🐈(非狗默认猫 emoji)豆豆。

场景六:删除

打开任意宠物 → “🗑 删除宠物"→ 确认(提示连同全部记录)→ toast"🗑 已删除”,统计减 1。

场景七:数据持久性

杀应用重开 → 数据仍在。

六、模式抽象:"1 主 N 从 + 时间序列"通用模板

6.1 模型

┌───────────────────────────────────────────────┐
│  主体档案表(如 pet)                           │
│  id / 标识字段 / 状态字段(next_vaccine)         │
│  ── 主体是谁、当前状态                          │
└───────┬────────────────────┬──────────────────┘
        │ 1:N                 │ 1:N
┌───────┴───────┐    ┌────────┴───────┐
│ 时间序列 A     │    │ 时间序列 B      │
│(weight)      │    │(feed)        │
│ 值 + 时间戳    │    │ 文本 + 时间戳   │
│ 只追加        │    │ 只追加         │
└───────────────┘    └────────────────┘

6.2 六个约定

  1. 主体档案一张表:标识 + 状态字段集中;
  2. 异构时间序列分表:字段结构不同的记录(数值体重 vs 文本喂食)分开建表;
  3. 时间序列只追加:INSERT 为主,修改谨慎、删除按需;
  4. 最新值 = 倒序 + limit:最新体重/最近喂食的固定查询范式;
  5. 按日统计用 0 点边界:今日喂食、今日体重等每日聚合;
  6. 到期提醒 = 状态字段差值:next_vaccine 与 now 的天数差,负值=到期。

6.3 两个改写场景

场景 A:婴儿成长记录

宠物实例 婴儿改写
pet baby(婴儿:姓名/性别/生日/下次体检日)
weight_record 身高体重记录(height/weight)
feed_record 喂养记录(奶粉/辅食/奶量)
疫苗提醒 体检/疫苗接种提醒

场景 B:健身训练日志

宠物实例 健身改写
pet trainee(训练者:姓名/目标/下次体测日)
weight_record 体测记录(体重/体脂率)
feed_record 训练日志(动作/组数/重量)

场景 C:车辆/设备日志——主体 + 运行记录 + 加油/充电记录,模板复用。

三个场景的共同点:“一个主体 + 多维度时间记录”。掌握 32,任何"记录某个对象的多类历史数据"的业务都能落地。

七、三篇内容总对照

篇章 核心知识点 一句话总结
32-1 建表与数据层设计 1 主 2 从、最新值、今日统计 异构序列分表,三种查询形态
32-2 页面UI与操作实现 疫苗四态、双记录流、三弹窗 状态颜色化,多记录同屏
32-3 完整代码与运行效果 全量源码、操作剧本、N 从模板 主体+多维记录,改表名即复用

八、FAQ(三篇汇总补遗)

Q1:26-32 的从表语义完整总结?
A:26 事件、27 交易、28 交易+计划、29 审计、30 结构、31 时间片、32 多维度时间序列。从表从"记录型"演进到"结构型",再到"多从表并行"。本系列的数据库能力已覆盖个人应用的绝大多数建模场景

Q2:为什么体重和喂食不用一张"pet_log(type+value)"?
A:见上篇深度扩展——字段异构(REAL vs TEXT)分表类型安全。若将来类型很多且同构(加"洗澡/遛弯"都是文本备注),可考虑合并。按字段结构决定分合

Q3:疫苗提醒的阈值 30 天合理吗?
A:猫狗疫苗一般一年一次,提前 30 天提醒合理(准备预约)。不同宠物可配置不同阈值(next_vaccine 字段已有,阈值按需)。阈值常量 + 参数化筛选已支持自定义。

Q4:体重趋势图在哪里实现?
A:23 数据图表的 ChartCanvas 可画折线。把 weights 数组(时间升序)传给它即可。本实例记录流展示数据,图表扩展零数据层改动。

Q5:删除宠物真的需要三张表清理吗?
A:是——体重、喂食、宠物本体。循环删两从表 + 删主表。若启用物理外键 ON DELETE CASCADE 可自动,本实例保持显式两步(同 26 的讨论)。

Q6:avatar 头像怎么做?
A:photoAccessHelper 选图 → 复制到沙箱 → avatar 存路径 → image 组件加载。参考 21 签名板的保存与读取流程。本实例预留字段,实现留作练习。

九、结语

三篇文章完整走完了宠物档案的"设计 → 实现 → 复用"闭环:

  • 1 教你多从表建模:异构时间序列分表、最新值/今日/到期三种查询;
  • 2 教你多记录展示:疫苗四态徽标、双记录流、三录入弹窗;
  • 3 教你模式复用:1 主 N 从模板,改表名即覆盖婴儿记录、健身日志。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐