HarmonyOS NEXT AI 智能生活助手:AI 每日花语

前言

在第11篇中,我们实现了 AI 翻译助手。本文将实现一个更具趣味性和互动性的功能——AI 每日花语

花语 是花卉文化的核心,每种花都有独特的花语和寓意。AI 每日花语不仅告诉用户"这是什么花",还讲述花的故事、寓意、送花建议,甚至引用相关的诗句。

本文将实现:

  1. 花语查询:输入花名 → 获取花语、寓意、故事
  2. 每日推荐:首页随机展示一种花语
  3. FlowerManager:花语查询管理
  4. 花语卡片:精美的展示组件
  5. 收藏功能:收藏喜爱的花语

在这里插入图片描述

图1:每日花语页面布局展示

一、花语功能设计

1.1 页面布局

┌─────────────────────────┐
│ ← 返回    每日花语       │
├─────────────────────────┤
│                         │
│   ┌─────────────────┐   │
│   │                 │   │
│   │   [向日葵图标]    │   │  ← 花名/图标
│   │                 │   │
│   └─────────────────┘   │
│                         │
│   ┌─────────────────┐   │
│   │ 花语:沉默的爱    │   │
│   │                  │   │
│   │ 寓意:           │   │
│   │ 忠诚、希望、光明  │   │  ← 花语信息
│   │                  │   │
│   │ 送花建议:       │   │
│   │ 适合送给正在     │   │
│   │ 奋斗的朋友...    │   │
│   └─────────────────┘   │
│                         │
│   ┌─────────────────┐   │
│   │ [书图标] 历史故事  │   │  ← 故事区域
│   │ 向日葵的故事...   │   │
│   └─────────────────┘   │
│                         │
│   [分享] [收藏] [换一朵] │
├─────────────────────────┤
│  [换一朵]  [搜索其他花]  │
└─────────────────────────┘

1.2 技术架构

// 花语模块架构图(代码描述)
/*
  ┌─────────────────────────────────────────┐
  │           FlowerPage.ets                │
  │         (UI 展示与交互层)                │
  └─────────────────┬───────────────────────┘
                    │
  ┌─────────────────▼───────────────────────┐
  │         FlowerManager.ts                │
  │      (业务逻辑 + 缓存管理)               │
  └─────────────────┬───────────────────────┘
                    │
        ┌───────────┼───────────┐
        ▼           ▼           ▼
  ┌─────────┐ ┌─────────┐ ┌─────────┐
  │AIService│ │CacheMgr │ │relational│
  │(AI调用) │ │(缓存)   │ │Store     │
  └─────────┘ └─────────┘ └─────────┘
*/

二、数据模型

2.1 花语数据结构

// model/FlowerInfo.ts
export interface FlowerInfo {
  name: string;              // 花名
  alias?: string[];          // 别名
  language: string;          // 花语
  meaning: string;           // 寓意
  description: string;       // 描述
  suggestion: string;        // 送花建议
  story: string;             // 历史故事/传说
  poem?: string;             // 相关诗句
  season?: string;           // 花期
  color?: string;            // 花色
  origin?: string;           // 原产地
  imageUrl?: string;         // 图片 URL
  tags: string[];            // 标签
}

// 花语收藏
export interface FlowerFavorite {
  flowerName: string;
  language: string;
  savedAt: number;
}

// 花语数据库表定义
export const FLOWER_FAVORITES_TABLE = {
  tableName: 'flower_favorites',
  columns: [
    { name: 'id', type: 'INTEGER PRIMARY KEY AUTOINCREMENT' },
    { name: 'flower_name', type: 'TEXT NOT NULL' },
    { name: 'language', type: 'TEXT' },
    { name: 'saved_at', type: 'INTEGER' }
  ]
};

2.2 花语响应解析器

// parser/FlowerResponseParser.ts
export class FlowerResponseParser {
  static parse(content: string, defaultName: string): FlowerInfo {
    try {
      // 尝试提取 JSON
      const jsonMatch = content.match(/\{[\s\S]*\}/);
      if (jsonMatch) {
        const parsed = JSON.parse(jsonMatch[0]);
        return {
          name: parsed.name || defaultName,
          alias: parsed.alias || [],
          language: parsed.language || '暂无',
          meaning: parsed.meaning || '暂无',
          description: parsed.description || '',
          suggestion: parsed.suggestion || '暂无建议',
          story: parsed.story || '暂无相关故事',
          poem: parsed.poem,
          season: parsed.season,
          color: parsed.color,
          origin: parsed.origin,
          tags: parsed.tags || [defaultName]
        };
      }
    } catch {}

    // fallback
    return {
      name: defaultName,
      language: '暂无信息',
      meaning: '暂无信息',
      description: '',
      suggestion: '暂无建议',
      story: '暂无相关故事',
      tags: [defaultName]
    };
  }
}

三、FlowerManager

3.1 核心花语管理器

// ai/FlowerManager.ts
export class FlowerManager {
  private static instance: FlowerManager;
  private aiService = AIService.getInstance();
  private promptManager = PromptManager.getInstance();
  private cacheManager = CacheManager.getInstance();
  private favorites: FlowerFavorite[] = [];

  static getInstance(): FlowerManager {
    if (!FlowerManager.instance) {
      FlowerManager.instance = new FlowerManager();
    }
    return FlowerManager.instance;
  }

  // 查询花语(核心方法)
  async queryFlower(flowerName: string): Promise<FlowerInfo> {
    // 1. 检查缓存
    const cached = this.cacheManager.get<FlowerInfo>(`flower_${flowerName}`);
    if (cached) return cached;

    // 2. 构建 Prompt
    const prompt = this.promptManager.buildPrompt('flower', {
      flowerName: flowerName
    });

    // 3. 调用 AI(支持多 Provider:OpenAI/DeepSeek/Qwen/智谱/豆包)
    const response = await this.aiService.chat([
      { role: 'system', content: prompt },
      { role: 'user', content: `请查询"${flowerName}"的花语和相关信息` }
    ]);

    // 4. 解析 AI 返回的 JSON
    const flowerInfo = FlowerResponseParser.parse(response.content, flowerName);

    // 5. 写入缓存(内存 + 持久化)
    this.cacheManager.set(`flower_${flowerName}`, flowerInfo, 24 * 60 * 60 * 1000);
    await this.cacheManager.persist(`flower_${flowerName}`, flowerInfo);

    return flowerInfo;
  }

  // 获取每日推荐花语
  async getDailyFlower(): Promise<FlowerInfo> {
    const today = new Date();
    const seed = today.getFullYear() * 10000 + (today.getMonth() + 1) * 100 + today.getDate();
    const flowerList = [
      '向日葵', '玫瑰', '百合', '康乃馨', '郁金香',
      '雏菊', '紫罗兰', '勿忘我', '樱花', '梅花',
      '荷花', '牡丹', '茉莉', '栀子花', '风信子'
    ];
    const index = seed % flowerList.length;
    return this.queryFlower(flowerList[index]);
  }

  // 收藏花语
  addFavorite(flower: FlowerInfo): void {
    const existing = this.favorites.find(f => f.flowerName === flower.name);
    if (!existing) {
      this.favorites.unshift({
        flowerName: flower.name,
        language: flower.language,
        savedAt: Date.now()
      });
    }
  }

  // 取消收藏
  removeFavorite(flowerName: string): void {
    this.favorites = this.favorites.filter(f => f.flowerName !== flowerName);
  }

  // 是否已收藏
  isFavorite(flowerName: string): boolean {
    return this.favorites.some(f => f.flowerName === flowerName);
  }

  // 获取收藏列表
  getFavorites(): FlowerFavorite[] {
    return [...this.favorites];
  }

  // 搜索花
  async searchFlowers(keyword: string): Promise<string[]> {
    const commonFlowers = [
      '玫瑰', '百合', '郁金香', '向日葵', '康乃馨', '紫罗兰',
      '雏菊', '勿忘我', '樱花', '梅花', '荷花', '牡丹',
      '茉莉', '栀子花', '风信子', '薰衣草', '马蹄莲', '桔梗',
      '满天星', '铃兰', '海棠', '山茶', '杜鹃', '木兰'
    ];
    return commonFlowers.filter(f => f.includes(keyword));
  }
}

3.2 安全区适配工具

// utils/SafeAreaUtil.ts
import { display } from '@kit.ArkUI';

export class SafeAreaUtil {
  static getStatusBarHeight(): number {
    return AppStorage.get<number>('statusBarHeight') || 0;
  }

  static getNavBarHeight(): number {
    return AppStorage.get<number>('navBarHeight') || 0;
  }

  // 使用 display.getDefaultDisplaySync().densityPixels 转换 px 到 vp
  static px2vp(px: number): number {
    const density = display.getDefaultDisplaySync().densityPixels;
    return px / density;
  }
}

四、FlowerPage 页面

4.1 主页面实现

// pages/FlowerPage.ets
@Entry
@Component
struct FlowerPage {
  @State flowerName: string = '';
  @State flowerInfo: FlowerInfo | null = null;
  @State isLoading: boolean = false;
  @State isFavorite: boolean = false;
  @State searchResults: string[] = [];
  @State showSearch: boolean = false;
  @State cardScale: number = 0.95;
  @State cardOpacity: number = 0;
  @State statusBarHeight: number = 0;
  @State navBarHeight: number = 0;
  private flowerManager = FlowerManager.getInstance();

  aboutToAppear() {
    // 安全区适配
    this.statusBarHeight = AppStorage.get<number>('statusBarHeight') || 0;
    this.navBarHeight = AppStorage.get<number>('navBarHeight') || 0;
    this.loadDailyFlower();
  }

  async loadDailyFlower() {
    this.isLoading = true;
    try {
      const flower = await this.flowerManager.getDailyFlower();
      this.flowerInfo = flower;
      this.flowerName = flower.name;
      this.isFavorite = this.flowerManager.isFavorite(flower.name);
      this.animateCard();
    } catch {
      ToastUtil.show('获取花语失败');
    } finally {
      this.isLoading = false;
    }
  }

  async queryFlower(name: string) {
    if (!name.trim()) return;
    this.isLoading = true;
    this.flowerName = name;
    this.showSearch = false;

    try {
      const flower = await this.flowerManager.queryFlower(name);
      this.flowerInfo = flower;
      this.isFavorite = this.flowerManager.isFavorite(name);
      this.animateCard();
    } catch {
      ToastUtil.show('查询失败');
    } finally {
      this.isLoading = false;
    }
  }

  animateCard() {
    this.cardScale = 0.95;
    this.cardOpacity = 0;
    animateTo({ duration: 400, curve: Curve.FastOutSlowIn }, () => {
      this.cardScale = 1;
      this.cardOpacity = 1;
    });
  }

  build() {
    Column() {
      // 状态栏占位
      Row().width('100%').height(this.statusBarHeight);

      // 导航栏
      Row() {
        Image($r('app.media.ic_back')).width(24).height(24)
          .onClick(() => RouterUtil.back());
        Text('每日花语').fontSize(18).fontWeight(FontWeight.Bold).margin({ left: 12 });
        Blank();
        Image($r('app.media.ic_search')).width(22).height(22)
          .onClick(() => { this.showSearch = !this.showSearch; });
      }
      .width('100%').height(56).padding({ left: 16, right: 16 });

      // 搜索栏
      if (this.showSearch) {
        Row() {
          TextInput({ placeholder: '输入花名...', text: this.flowerName })
            .layoutWeight(1).height(40).backgroundColor('#F5F6FA')
            .borderRadius(20).padding({ left: 16 })
            .onChange((value: string) => {
              this.flowerName = value;
              this.flowerManager.searchFlowers(value).then(r => this.searchResults = r);
            })
            .onSubmit(() => this.queryFlower(this.flowerName));
          Button('查询').backgroundColor('#6C5CE7').fontColor(Color.White)
            .borderRadius(20).margin({ left: 8 })
            .onClick(() => this.queryFlower(this.flowerName));
        }
        .padding(16);

        // 搜索建议
        if (this.searchResults.length > 0) {
          Column() {
            ForEach(this.searchResults, (name: string) => {
              Text(name).fontSize(15).padding(12).width('100%')
                .onClick(() => this.queryFlower(name));
            }, (name: string) => name);
          }
          .backgroundColor(Color.White).borderRadius(12).margin({ left: 16, right: 16 });
        }
      }

      // 加载状态
      if (this.isLoading) {
        LoadingView({ text: '正在查询花语...' }).layoutWeight(1);
        return;
      }

      // 花语内容
      if (this.flowerInfo) {
        Scroll() {
          Column() {
            // 花名头部
            Column() {
              Text(this.flowerInfo.name)
                .fontSize(32).fontWeight(FontWeight.Bold).fontColor('#2D3436');
              if (this.flowerInfo.alias?.length) {
                Text(`别名:${this.flowerInfo.alias.join('、')}`)
                  .fontSize(14).fontColor(Color.Gray).margin({ top: 4 });
              }
            }
            .width('100%').padding(24)
            .backgroundColor(LinearGradient.linearGradient({
              direction: GradientDirection.Bottom,
              colors: [['#6C5CE7', 0], ['#A29BFE', 0.5], ['#E8E8FF', 1]]
            }))
            .borderRadius({ bottomLeft: 24, bottomRight: 24 });

            // 花语卡片
            Column() {
              // 花语
              this.infoRow('花语', this.flowerInfo.language);
              // 寓意
              this.infoRow('寓意', this.flowerInfo.meaning);
              // 送花建议
              this.infoRow('送花建议', this.flowerInfo.suggestion);
            }
            .width('100%').padding(16)
            .backgroundColor(Color.White).borderRadius(16)
            .margin(16)
            .opacity(this.cardOpacity).scale({ x: this.cardScale, y: this.cardScale });

            // 历史故事
            Column() {
              Text('历史故事').fontSize(16).fontWeight(FontWeight.Bold)
                .width('100%').margin({ bottom: 8 });
              Text(this.flowerInfo.story).fontSize(15).lineHeight(24).fontColor('#636E72');
            }
            .width('100%').padding(16)
            .backgroundColor(Color.White).borderRadius(16)
            .margin({ left: 16, right: 16, bottom: 16 });

            // 相关诗句
            if (this.flowerInfo.poem) {
              Column() {
                Text('相关诗句').fontSize(16).fontWeight(FontWeight.Bold)
                  .width('100%').margin({ bottom: 8 });
                Text(this.flowerInfo.poem).fontSize(15).fontStyle(FontStyle.Italic)
                  .fontColor('#6C5CE7').lineHeight(24);
              }
              .width('100%').padding(16)
              .backgroundColor('#F8F6FF').borderRadius(16)
              .margin({ left: 16, right: 16, bottom: 16 });
            }

            // 操作按钮
            Row() {
              this.actionButton('收藏', this.isFavorite ? '#E17055' : '#B2BEC3', () => {
                if (this.isFavorite) {
                  this.flowerManager.removeFavorite(this.flowerInfo!.name);
                } else {
                  this.flowerManager.addFavorite(this.flowerInfo!);
                }
                this.isFavorite = !this.isFavorite;
              });
              this.actionButton('换一朵', '#6C5CE7', () => {
                this.loadDailyFlower();
              });
              this.actionButton('分享', '#0984E3', () => {
                this.shareFlower();
              });
            }
            .width('100%').padding(16);
          }
        }
        .layoutWeight(1);
      }

      // 底部导航栏占位
      Row().width('100%').height(this.navBarHeight);
    }
    .width('100%').height('100%').backgroundColor('#F5F6FA');
  }

  @Builder
  infoRow(label: string, value: string) {
    Column() {
      Text(label).fontSize(13).fontColor('#6C5CE7').fontWeight(FontWeight.Medium);
      Text(value).fontSize(15).fontColor('#2D3436').lineHeight(22)
        .margin({ top: 4 }).width('100%');
    }
    .width('100%').padding({ top: 8, bottom: 8 })
    .border({ bottom: { width: 0.5, color: '#F0F0F0' } });
  }

  @Builder
  actionButton(label: string, color: string, action: () => void) {
    Button() {
      Text(label).fontSize(13).fontColor(Color.White);
    }
    .backgroundColor(color).borderRadius(20).height(36)
    .layoutWeight(1).margin({ left: 4, right: 4 })
    .onClick(() => action());
  }

  shareFlower() {
    if (!this.flowerInfo) return;
    const shareText = `${this.flowerInfo.name}${this.flowerInfo.language}\n\n花语:${this.flowerInfo.language}\n寓意:${this.flowerInfo.meaning}\n\n来自 HarmonyAI 智能生活助手`;
    const clipboard = getContext(this).clipboard;
    clipboard.set({ primary: shareText });
    ToastUtil.show('已复制分享内容');
  }
}

五、Prompt 模板

5.1 flower.md

---
name: flower
version: 1.2.0
description: AI 每日花语查询
---

你是一位博学的花语专家和植物文化研究者。请查询「{{flowerName}}」的详细信息。

## 输出要求

请用以下 JSON 格式回答:

{
  "name": "花名",
  "alias": ["别名1", "别名2"],
  "language": "一句话花语概括",
  "meaning": "详细寓意解析,2-3句话",
  "description": "花卉描述",
  "suggestion": "送花建议及适用场合",
  "story": "相关的历史故事、神话传说或文化典故,100-150字",
  "poem": "一首与该花相关的经典诗句",
  "season": "花期季节",
  "color": "主要花色",
  "origin": "原产地",
  "tags": ["花名", "花语", "花卉文化"]
}

## 注意事项

1. 花语须符合传统花语文化
2. 故事需有出处,不可编造
3. 诗句须标注出处
4. 送花建议要实用、具体

六、花期与花语对照表

6.1 四季花期速查

export const SEASONAL_FLOWERS: Record<string, FlowerInfo[]> = {
  spring: [
    { name: '樱花', language: '生命之美', season: '3-4月', meaning: '生命、美丽' },
    { name: '郁金香', language: '完美的爱情', season: '3-5月', meaning: '博爱、体贴' },
    { name: '牡丹', language: '富贵吉祥', season: '4-5月', meaning: '圆满、浓情' }
  ],
  summer: [
    { name: '荷花', language: '出淤泥而不染', season: '6-8月', meaning: '纯洁、坚贞' },
    { name: '向日葵', language: '沉默的爱', season: '7-8月', meaning: '忠诚、希望' },
    { name: '百合', language: '百年好合', season: '6-7月', meaning: '顺利、祝福' }
  ],
  autumn: [
    { name: '菊花', language: '清净高洁', season: '9-11月', meaning: '长寿、隐逸' },
    { name: '桂花', language: '收获与吉祥', season: '9-10月', meaning: '吉祥、友好' },
    { name: '木芙蓉', language: '纤细之美', season: '10-11月', meaning: '贞操、高洁' }
  ],
  winter: [
    { name: '梅花', language: '坚强不屈', season: '12-2月', meaning: '坚韧、高洁' },
    { name: '水仙', language: '纯洁高雅', season: '1-2月', meaning: '自尊、高雅' },
    { name: '山茶', language: '理想的爱', season: '1-3月', meaning: '谦让、魅力' }
  ]
};
季节 代表花卉 花期 关键词
春季 樱花、郁金香、牡丹 3-5月 生命、爱情、富贵
夏季 荷花、向日葵、百合 6-8月 纯洁、忠诚、祝福
秋季 菊花、桂花、木芙蓉 9-11月 高洁、吉祥、美丽
冬季 梅花、水仙、山茶 12-2月 坚韧、高雅、谦让

选花建议:送花时不仅要考虑花语,还要结合季节性。应季花卉不仅价格更优,花期也更持久。

6.2 花色与花语关系

export const COLOR_FLOWER_MEANING: Record<string, string> = {
  '红色': '热情、爱情、尊重',
  '粉色': '温柔、浪漫、幸福',
  '白色': '纯洁、真诚、谦逊',
  '黄色': '友谊、快乐、温暖',
  '紫色': '高贵、神秘、优雅',
  '蓝色': '宁静、深邃、永恒',
  '橙色': '活力、热情、希望',
  '绿色': '平和、生机、健康'
};

export function getFlowerMeaningByColor(flower: string, color: string): string {
  const base = COLOR_FLOWER_MEANING[color] || '美好';
  return `${flower}${color}色):${base},适合表达${getOccasion(color)}的情感`;
}

function getOccasion(color: string): string {
  const occasions: Record<string, string> = {
    '红色': '热烈真挚',
    '粉色': '甜蜜温馨',
    '白色': '纯洁崇高',
    '黄色': '阳光积极',
    '紫色': '深邃浪漫',
    '蓝色': '沉静安宁'
  };
  return occasions[color] || '美好';
}

七、送花场景指南

7.1 不同场合的送花建议

场合 推荐花卉 花语 禁忌
生日 康乃馨、百合 祝福、健康 白色系慎选
求婚/表白 红玫瑰、郁金香 爱情、热恋 黄色玫瑰(分手)
探病 向日葵、康乃馨 康复、温暖 浓郁花香花卉
母亲节 康乃馨、萱草 感恩、母爱
教师节 向日葵、剑兰 感恩、尊敬
开业 发财树、百合 财运、顺利 白色花卉
道歉 黄玫瑰、风信子 歉意、悔过 红玫瑰
葬礼 白菊、白百合 哀悼、怀念 鲜艳色彩

7.2 送花数量寓意

export const FLOWER_COUNT_MEANING: Record<number, string> = {
  1: '一见钟情、唯一的爱',
  11: '一心一意',
  19: '一世长久',
  33: '三生三世',
  99: '天长地久',
  101: '百里挑一',
  520: '我爱你',
  999: '长长久久',
  1314: '一生一世'
};

export function getCountSuggestion(relationship: string): number {
  const suggestions: Record<string, number> = {
    '恋人': 99,
    '配偶': 33,
    '朋友': 11,
    '父母': 19,
    '同事': 6,
    '长辈': 12
  };
  return suggestions[relationship] || 11;
}

数量讲究:送花数量在中国文化中有特定的寓意。一般来说,双数花朵在中国传统中更吉利,但表白常用 99 朵寓意长久。

7.3 保鲜与养护

export class FlowerCareGuide {
  static getCareTips(flowerType: string): string[] {
    const tips: Record<string, string[]> = {
      'rose': ['每天换水', '斜剪根茎', '避免阳光直射', '远离水果'],
      'tulip': ['浅水养护', '避免高温', '花茎会继续生长', '不要与其他花混插'],
      'sunflower': ['深水养护', '每天换水', '添加保鲜剂', '避免风吹'],
      'lily': ['去除花粉', '浅水养护', '避免猫接触', '花朵开放后摘除花蕊'],
      'default': ['每天换水', '斜剪根茎45度', '去除水面以下叶片', '放在阴凉通风处']
    };
    return tips[flowerType] || tips['default'];
  }

  static readonly WATER_TEMPERATURE: Record<string, string> = {
    '温水': '郁金香、百合、风信子',
    '凉水': '玫瑰、菊花、康乃馨',
    '冰水': '兰花、马蹄莲'
  };
}
花卉类型 换水频率 剪根周期 保鲜期
玫瑰 每天 每2天 7-10天
百合 2天 每3天 10-14天
郁金香 每天 不需要 5-7天
康乃馨 2天 每3天 14-21天
向日葵 每天 每天 7-10天
菊花 3天 每5天 14-21天

八、花语文化在APP中的运用

8.1 每日花语推送机制

export class DailyFlowerPush {
  private flowerManager = FlowerManager.getInstance();
  private pushDate: string = '';

  async checkAndPush(): Promise<void> {
    const today = new Date().toDateString();
    if (this.pushDate === today) return;

    const flower = await this.flowerManager.getDailyFlower();
    this.pushDate = today;
    
    // 保存到本地通知
    await this.scheduleNotification(flower);
  }

  private async scheduleNotification(flower: FlowerInfo): Promise<void> {
    // 使用 HarmonyOS 通知能力
    const notificationContent = {
      title: '每日花语',
      text: `今日推荐:${flower.name}${flower.language}`,
      additionalText: flower.meaning
    };
    // 发送通知...
  }
}

8.2 花语社交分享

export class FlowerShareUtil {
  static buildShareCard(flower: FlowerInfo): string {
    return `${flower.name}
    
花语:${flower.language}
寓意:${flower.meaning}
送花建议:${flower.suggestion}

${flower.story?.slice(0, 100)}...

—— 来自 HarmonyAI 智能生活助手`;
  }

  static async shareToSocial(flower: FlowerInfo): Promise<void> {
    const text = this.buildShareCard(flower);
    try {
      await getContext().share(text);
    } catch (error) {
      console.error('Share failed:', error);
    }
  }
}

九、数据持久化与缓存

9.1 使用 relationalStore 存储收藏

// database/FlowerDatabase.ts
import { relationalStore } from '@kit.ArkData';

export class FlowerDatabase {
  private static instance: FlowerDatabase;
  private rdbStore: relationalStore.RdbStore | null = null;

  static getInstance(): FlowerDatabase {
    if (!FlowerDatabase.instance) {
      FlowerDatabase.instance = new FlowerDatabase();
    }
    return FlowerDatabase.instance;
  }

  async init(context: Context): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: 'flower.db',
      securityLevel: relationalStore.SecurityLevel.S1
    };
    this.rdbStore = await relationalStore.getRdbStore(context, config);
    await this.rdbStore?.executeSql(`
      CREATE TABLE IF NOT EXISTS flower_favorites (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        flower_name TEXT NOT NULL,
        language TEXT,
        saved_at INTEGER
      )
    `);
  }

  async addFavorite(favorite: FlowerFavorite): Promise<number> {
    const bucket: relationalStore.ValuesBucket = {
      flower_name: favorite.flowerName,
      language: favorite.language,
      saved_at: favorite.savedAt
    };
    return await this.rdbStore?.insert('flower_favorites', bucket) || -1;
  }

  async getAllFavorites(): Promise<FlowerFavorite[]> {
    const predicates = new relationalStore.RdbPredicates('flower_favorites');
    predicates.orderByDesc('saved_at');
    const resultSet = await this.rdbStore?.query(predicates);
    const list: FlowerFavorite[] = [];
    while (resultSet?.goToNextRow()) {
      list.push({
        flowerName: resultSet.getString(resultSet.getColumnIndex('flower_name')),
        language: resultSet.getString(resultSet.getColumnIndex('language')),
        savedAt: resultSet.getLong(resultSet.getColumnIndex('saved_at'))
      });
    }
    resultSet?.close();
    return list;
  }
}

9.2 CacheManager 缓存花语数据

// cache/CacheManager.ts
export class CacheManager {
  private static instance: CacheManager;
  private memoryCache: Map<string, CacheEntry> = new Map();

  static getInstance(): CacheManager {
    if (!CacheManager.instance) {
      CacheManager.instance = new CacheManager();
    }
    return CacheManager.instance;
  }

  set<T>(key: string, value: T, ttl: number = 24 * 60 * 60 * 1000): void {
    this.memoryCache.set(key, {
      data: value,
      expireAt: Date.now() + ttl
    });
  }

  get<T>(key: string): T | null {
    const entry = this.memoryCache.get(key);
    if (!entry) return null;
    if (Date.now() > entry.expireAt) {
      this.memoryCache.delete(key);
      return null;
    }
    return entry.data as T;
  }

  async persist<T>(key: string, value: T): Promise<void> {
    const pref = await getPreferences(getContext(), 'flower_cache');
    await pref.put(key, JSON.stringify(value));
    await pref.flush();
  }

  clear(): void {
    this.memoryCache.clear();
  }
}

interface CacheEntry {
  data: unknown;
  expireAt: number;
}

十、Git 提交

git add .
git commit -m "feat(flower): 完成 AI 每日花语功能

- 实现 FlowerManager(查询/每日推荐/搜索)
- 实现 FlowerPage(花语展示/搜索/收藏)
- 设计花语数据模型
- 实现每日推荐算法
- 实现花语卡片动画
- 实现收藏功能
- 集成 relationalStore 持久化收藏数据
- 实现 CacheManager 内存缓存 + 持久化缓存
- 支持 OpenAI/DeepSeek/Qwen/智谱/豆包 多 Provider
- 实现安全区适配(AppStorage + display)
- 使用 SVG 矢量图替代 emoji
- 实现 PromptManager 版本控制

Co-Authored-By: AtomCode (deepseek-v4-flash) <noreply@atomgit.com>"

git tag v0.1.1

总结

本文实现了 AI 每日花语 功能。核心要点:

  1. 花语查询:输入花名获取花语、寓意、故事、诗句
  2. 每日推荐:每天自动推荐一种花
  3. FlowerManager:查询 + 缓存 + 收藏
  4. 精美卡片:渐变背景 + 入场动画
  5. 搜索功能:内置 24 种常见花卉
  6. 多 Provider 支持:OpenAI、DeepSeek、Qwen、智谱、豆包
  7. PromptManager:独立管理 prompt,支持版本控制
  8. 安全区适配:通过 AppStorage 获取 statusBarHeight 和 navBarHeight
  9. 数据持久化:使用 relationalStore 保存收藏数据
  10. 双层缓存:CacheManager 内存缓存 + 持久化缓存
  11. SVG 图标:所有图标使用矢量图,不使用 emoji

如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续创作的动力!


相关资源

Logo

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

更多推荐