一、应用概述

1.1 应用简介

名言警句(Quote Generator)是一款精选名言展示与管理的应用,内置涵盖中外经典、哲学思想、励志格言、人生感悟等多类别的高质量名言库,支持分类筛选、随机展示、收藏管理、自动轮播和个性化展示。该应用以HarmonyOS NEXT的ArkTS框架为基础,深入展示了结构化数据管理、分类过滤算法、随机索引算法、收藏管理、定时器自动播放和数据持久化等关键技术。

1.2 核心功能

功能模块 功能描述 技术实现 设计考量
名言库 内置100+条精选名言 结构化数据数组 多维度分类标签
分类筛选 按5个类别筛选 条件过滤 + 计算属性 支持多选和全部
随机展示 随机切换名言 随机索引算法 + 避免重复 不完全随机,有历史记录
收藏管理 收藏/取消收藏 数组增删操作 持久化存储
自动轮播 定时切换展示 setInterval定时器 可调节间隔时间
字体缩放 可调节字体大小 状态变量绑定样式 保存用户偏好
每日一句 每天展示不同名言 以日期为种子的伪随机 每天固定显示同一句
分享功能 复制名言到剪贴板 剪贴板API 格式化为精美文本

1.3 应用架构

名言警句应用采用分层架构:

  1. UI表现层:名言展示卡片、分类筛选标签、收藏按钮、轮播控制面板、字体缩放滑块。
  2. 业务逻辑层:名言管理引擎、随机算法、分类过滤引擎、收藏管理器、轮播控制器。
  3. 数据持久层:使用Preferences API存储收藏列表、轮播设置和字体偏好。

二、数据管理

2.1 名言数据结构

interface Quote {
  id: string;              // 唯一标识
  text: string;            // 名言正文
  author: string;          // 作者
  category: string;        // 分类
  subCategory: string;     // 子分类
  tags: string[];          // 标签数组
  source: string;          // 出处(如书籍、演讲)
  year: number;            // 年份
  language: string;        // 原始语言
  popularity: number;      // 流行度评分(1-5)
}
interface QuoteCategory {
  id: string;
  name: string;
  icon: string;
  description: string;
  count: number;           // 该分类下的名言数量(动态计算)
}

2.2 名言库数据

class QuoteDatabase {
  private static readonly QUOTES: Quote[] = [
    // === 人生感悟 ===
    { id: 'life_001', text: '生活就像一盒巧克力,你永远不知道下一颗是什么味道。', author: '阿甘正传', category: '人生', subCategory: '生活感悟', tags: ['生活', '未知', '电影'], source: '《阿甘正传》', year: 1994, language: '中文', popularity: 5 },
    { id: 'life_002', text: '人生最重要的不是所站的位置,而是所朝的方向。', author: '奥利弗·温德尔·霍姆斯', category: '人生', subCategory: '方向', tags: ['人生', '方向', '目标'], source: '', year: 0, language: '英文', popularity: 4 },
    { id: 'life_003', text: '活着就是为了改变世界,难道还有其他原因吗?', author: '史蒂夫·乔布斯', category: '人生', subCategory: '使命', tags: ['人生', '改变', '使命'], source: '', year: 0, language: '英文', popularity: 5 },
    { id: 'life_004', text: '人生如逆旅,我亦是行人。', author: '苏轼', category: '人生', subCategory: '豁达', tags: ['人生', '豁达', '古诗词'], source: '《临江仙·送钱穆父》', year: 1091, language: '中文', popularity: 5 },
    { id: 'life_005', text: '人生天地间,忽如远行客。', author: '佚名', category: '人生', subCategory: '感慨', tags: ['人生', '时光', '古诗'], source: '《古诗十九首》', year: 0, language: '中文', popularity: 4 },
    
    // === 励志格言 ===
    { id: 'inspire_001', text: '千里之行,始于足下。', author: '老子', category: '励志', subCategory: '行动', tags: ['行动', '开始', '哲学'], source: '《道德经》', year: 0, language: '中文', popularity: 5 },
    { id: 'inspire_002', text: '成功不是终点,失败也不是终结,唯有勇气才是永恒。', author: '温斯顿·丘吉尔', category: '励志', subCategory: '勇气', tags: ['成功', '失败', '勇气'], source: '', year: 0, language: '英文', popularity: 5 },
    { id: 'inspire_003', text: '天行健,君子以自强不息。', author: '孔子', category: '励志', subCategory: '自强', tags: ['自强', '奋斗', '易经'], source: '《周易》', year: 0, language: '中文', popularity: 5 },
    { id: 'inspire_004', text: '不要问你的国家能为你做什么,而要问你能为你的国家做什么。', author: '约翰·F·肯尼迪', category: '励志', subCategory: '奉献', tags: ['奉献', '责任', '国家'], source: '就职演说', year: 1961, language: '英文', popularity: 4 },
    { id: 'inspire_005', text: '世上无难事,只怕有心人。', author: '王阳明', category: '励志', subCategory: '坚持', tags: ['坚持', '努力', '信念'], source: '', year: 0, language: '中文', popularity: 5 },
    
    // === 哲理思考 ===
    { id: 'philo_001', text: '我思故我在。', author: '勒内·笛卡尔', category: '哲理', subCategory: '存在', tags: ['思考', '存在', '哲学'], source: '《方法论》', year: 1637, language: '拉丁文', popularity: 5 },
    { id: 'philo_002', text: '存在即合理。', author: '格奥尔格·黑格尔', category: '哲理', subCategory: '理性', tags: ['存在', '理性', '哲学'], source: '《法哲学原理》', year: 1820, language: '德语', popularity: 5 },
    { id: 'philo_003', text: '幸福就是身体健康,记忆模糊。', author: '阿尔伯特·爱因斯坦', category: '哲理', subCategory: '幸福', tags: ['幸福', '健康', '智慧'], source: '', year: 0, language: '英文', popularity: 4 },
    { id: 'philo_004', text: '知之为知之,不知为不知,是知也。', author: '孔子', category: '哲理', subCategory: '认知', tags: ['认知', '智慧', '学习'], source: '《论语》', year: 0, language: '中文', popularity: 5 },
    { id: 'philo_005', text: '自由不是做你想做的事,而是不做你不想做的事。', author: '让-雅克·卢梭', category: '哲理', subCategory: '自由', tags: ['自由', '选择', '哲学'], source: '', year: 0, language: '法语', popularity: 4 },
    
    // === 文学经典 ===
    { id: 'lit_001', text: '生存还是毁灭,这是一个问题。', author: '威廉·莎士比亚', category: '文学', subCategory: '戏剧', tags: ['生存', '选择', '经典'], source: '《哈姆雷特》', year: 1603, language: '英文', popularity: 5 },
    { id: 'lit_002', text: '幸福的家庭都是相似的,不幸的家庭各有各的不幸。', author: '列夫·托尔斯泰', category: '文学', subCategory: '小说', tags: ['家庭', '幸福', '经典'], source: '《安娜·卡列尼娜》', year: 1877, language: '俄语', popularity: 5 },
    { id: 'lit_003', text: '人的一切痛苦,本质上都是对自己无能的愤怒。', author: '王小波', category: '文学', subCategory: '杂文', tags: ['痛苦', '愤怒', '人性'], source: '《沉默的大多数》', year: 1997, language: '中文', popularity: 5 },
    { id: 'lit_004', text: '世界上只有一种真正的英雄主义,那就是在认清生活真相之后依然热爱生活。', author: '罗曼·罗兰', category: '文学', subCategory: '英雄', tags: ['英雄主义', '生活', '热爱'], source: '《米开朗基罗传》', year: 1906, language: '法语', popularity: 5 },
    { id: 'lit_005', text: '满纸荒唐言,一把辛酸泪。都云作者痴,谁解其中味。', author: '曹雪芹', category: '文学', subCategory: '小说', tags: ['经典', '红楼梦', '感慨'], source: '《红楼梦》', year: 1791, language: '中文', popularity: 5 },
    
    // === 名人智慧 ===
    { id: 'wise_001', text: '想象力比知识更重要。', author: '阿尔伯特·爱因斯坦', category: '智慧', subCategory: '创新', tags: ['想象力', '知识', '创新'], source: '', year: 0, language: '英文', popularity: 5 },
    { id: 'wise_002', text: '己所不欲,勿施于人。', author: '孔子', category: '智慧', subCategory: '处世', tags: ['处世', '道德', '黄金法则'], source: '《论语》', year: 0, language: '中文', popularity: 5 },
    { id: 'wise_003', text: '学而不思则罔,思而不学则殆。', author: '孔子', category: '智慧', subCategory: '学习', tags: ['学习', '思考', '教育'], source: '《论语》', year: 0, language: '中文', popularity: 5 },
    { id: 'wise_004', text: '在时间的钟上,只有两个字——现在。', author: '威廉·莎士比亚', category: '智慧', subCategory: '时间', tags: ['时间', '当下', '珍惜'], source: '', year: 0, language: '英文', popularity: 4 },
    { id: 'wise_005', text: '知识就是力量。', author: '弗朗西斯·培根', category: '智慧', subCategory: '知识', tags: ['知识', '力量', '名言'], source: '《沉思录》', year: 1597, language: '英文', popularity: 5 },
  ];
  
  getAll(): Quote[] {
    return [...QuoteDatabase.QUOTES];
  }
  
  getById(id: string): Quote | undefined {
    return QuoteDatabase.QUOTES.find(q => q.id === id);
  }
  
  getByCategory(category: string): Quote[] {
    return QuoteDatabase.QUOTES.filter(q => q.category === category);
  }
  
  getByAuthor(author: string): Quote[] {
    return QuoteDatabase.QUOTES.filter(q => q.author.includes(author));
  }
  
  search(query: string): Quote[] {
    const q = query.toLowerCase();
    return QuoteDatabase.QUOTES.filter(quote =>
      quote.text.toLowerCase().includes(q) ||
      quote.author.toLowerCase().includes(q) ||
      quote.category.toLowerCase().includes(q) ||
      quote.tags.some(tag => tag.toLowerCase().includes(q))
    );
  }
  
  getCategories(): QuoteCategory[] {
    const categoryMap = new Map<string, number>();
    for (const quote of QuoteDatabase.QUOTES) {
      categoryMap.set(quote.category, (categoryMap.get(quote.category) || 0) + 1);
    }
    
    return [
      { id: 'all', name: '全部', icon: '📚', description: '所有名言', count: QuoteDatabase.QUOTES.length },
      { id: '人生', name: '人生感悟', icon: '🌅', description: '关于人生的思考', count: categoryMap.get('人生') || 0 },
      { id: '励志', name: '励志格言', icon: '🔥', description: '激励人心的格言', count: categoryMap.get('励志') || 0 },
      { id: '哲理', name: '哲理思考', icon: '💭', description: '深刻的哲学思考', count: categoryMap.get('哲理') || 0 },
      { id: '文学', name: '文学经典', icon: '📖', description: '文学名著中的经典', count: categoryMap.get('文学') || 0 },
      { id: '智慧', name: '名人智慧', icon: '💡', description: '名人的智慧箴言', count: categoryMap.get('智慧') || 0 },
    ];
  }
}

三、随机展示算法

3.1 随机索引算法

class RandomQuoteSelector {
  private history: string[] = []; // 最近展示过的名言ID列表
  private readonly HISTORY_SIZE = 10; // 避免短期内重复展示同一句
  
  // 完全随机选择
  selectRandom(quotes: Quote[]): Quote | null {
    if (quotes.length === 0) return null;
    const index = Math.floor(Math.random() * quotes.length);
    return quotes[index];
  }
  
  // 带历史记录的随机选择(避免重复)
  selectRandomWithHistory(quotes: Quote[]): Quote | null {
    if (quotes.length === 0) return null;
    
    // 如果所有名言都在历史中,清空历史
    if (this.history.length >= quotes.length) {
      this.history = [];
    }
    
    // 从未在历史中的名言中随机选择
    const available = quotes.filter(q => !this.history.includes(q.id));
    const selected = available[Math.floor(Math.random() * available.length)];
    
    if (selected) {
      this.history.push(selected.id);
      if (this.history.length > this.HISTORY_SIZE) {
        this.history.shift();
      }
    }
    
    return selected || this.selectRandom(quotes);
  }
  
  // 每日一句:以日期为种子生成伪随机数
  selectDailyQuote(quotes: Quote[]): Quote | null {
    if (quotes.length === 0) return null;
    
    const today = new Date();
    const seed = today.getFullYear() * 10000 + 
                 (today.getMonth() + 1) * 100 + 
                 today.getDate();
    
    // 使用线性同余生成器(LCG)生成伪随机数
    const lcg = (seed: number): number => {
      return (seed * 1103515245 + 12345) & 0x7fffffff;
    };
    
    const randomIndex = lcg(seed) % quotes.length;
    return quotes[randomIndex];
  }
  
  // 加权随机选择(根据流行度加权)
  selectWeightedRandom(quotes: Quote[]): Quote | null {
    if (quotes.length === 0) return null;
    
    const totalWeight = quotes.reduce((sum, q) => sum + q.popularity, 0);
    let random = Math.random() * totalWeight;
    
    for (const quote of quotes) {
      random -= quote.popularity;
      if (random <= 0) return quote;
    }
    
    return quotes[quotes.length - 1];
  }
}

3.2 分类过滤引擎

class QuoteFilterEngine {
  private activeFilters: Set<string> = new Set();
  private searchQuery: string = '';
  private authorFilter: string = '';
  
  setCategoryFilter(category: string): void {
    if (category === 'all') {
      this.activeFilters.clear();
    } else {
      this.activeFilters.add(category);
    }
  }
  
  toggleCategoryFilter(category: string): void {
    if (this.activeFilters.has(category)) {
      this.activeFilters.delete(category);
    } else {
      this.activeFilters.add(category);
    }
  }
  
  setSearchQuery(query: string): void {
    this.searchQuery = query.toLowerCase();
  }
  
  setAuthorFilter(author: string): void {
    this.authorFilter = author.toLowerCase();
  }
  
  // 多维度过滤
  filter(quotes: Quote[]): Quote[] {
    let result = [...quotes];
    
    // 分类过滤
    if (this.activeFilters.size > 0) {
      result = result.filter(q => this.activeFilters.has(q.category));
    }
    
    // 搜索过滤
    if (this.searchQuery) {
      result = result.filter(q =>
        q.text.toLowerCase().includes(this.searchQuery) ||
        q.author.toLowerCase().includes(this.searchQuery) ||
        q.tags.some(tag => tag.toLowerCase().includes(this.searchQuery))
      );
    }
    
    // 作者过滤
    if (this.authorFilter) {
      result = result.filter(q => 
        q.author.toLowerCase().includes(this.authorFilter)
      );
    }
    
    return result;
  }
  
  // 获取过滤后的统计信息
  getFilterStats(quotes: Quote[]): FilterStats {
    const filtered = this.filter(quotes);
    const authors = new Set(filtered.map(q => q.author));
    const categories = new Set(filtered.map(q => q.category));
    
    return {
      totalCount: filtered.length,
      authorCount: authors.size,
      categoryCount: categories.size,
      averagePopularity: filtered.length > 0 
        ? Math.round(filtered.reduce((s, q) => s + q.popularity, 0) / filtered.length * 10) / 10
        : 0
    };
  }
}
interface FilterStats {
  totalCount: number;
  authorCount: number;
  categoryCount: number;
  averagePopularity: number;
}

四、收藏管理器

4.1 收藏数据管理

class FavoriteManager {
  private favorites: Set<string> = new Set();
  private prefs: preferences.Preferences | null = null;
  private static readonly STORAGE_KEY = 'favorite_quotes';
  
  async init(context: Context): Promise<void> {
    this.prefs = await preferences.getPreferences(context, 'quote_prefs');
    await this.load();
  }
  
  toggleFavorite(quoteId: string): boolean {
    if (this.favorites.has(quoteId)) {
      this.favorites.delete(quoteId);
      this.save();
      return false; // 已取消收藏
    } else {
      this.favorites.add(quoteId);
      this.save();
      return true; // 已收藏
    }
  }
  
  isFavorite(quoteId: string): boolean {
    return this.favorites.has(quoteId);
  }
  
  getFavorites(quotes: Quote[]): Quote[] {
    return quotes.filter(q => this.favorites.has(q.id));
  }
  
  getFavoriteCount(): number {
    return this.favorites.size;
  }
  
  async removeAll(): Promise<void> {
    this.favorites.clear();
    await this.save();
  }
  
  private getFavoritesArray(): string[] {
    return Array.from(this.favorites);
  }
  
  private async load(): Promise<void> {
    if (!this.prefs) return;
    const json = await this.prefs.get(FavoriteManager.STORAGE_KEY, '[]');
    try {
      const arr = JSON.parse(json) as string[];
      this.favorites = new Set(arr);
    } catch {
      this.favorites = new Set();
    }
  }
  
  private async save(): Promise<void> {
    if (!this.prefs) return;
    await this.prefs.put(FavoriteManager.STORAGE_KEY, JSON.stringify(this.getFavoritesArray()));
    await this.prefs.flush();
  }
}

五、自动轮播系统

5.1 轮播控制器

class AutoPlayController {
  private timerId: number | null = null;
  private interval: number = 5000; // 默认5秒
  private isPlaying: boolean = false;
  private onNext: (() => void) | null = null;
  
  setInterval(ms: number): void {
    this.interval = ms;
    if (this.isPlaying) {
      this.stop();
      this.start();
    }
  }
  
  setOnNext(callback: () => void): void {
    this.onNext = callback;
  }
  
  start(): void {
    if (this.isPlaying) return;
    this.isPlaying = true;
    this.timerId = setInterval(() => {
      this.onNext?.();
    }, this.interval);
  }
  
  stop(): void {
    if (this.timerId !== null) {
      clearInterval(this.timerId);
      this.timerId = null;
    }
    this.isPlaying = false;
  }
  
  toggle(): boolean {
    if (this.isPlaying) {
      this.stop();
    } else {
      this.start();
    }
    return this.isPlaying;
  }
  
  getStatus(): { isPlaying: boolean; interval: number } {
    return { isPlaying: this.isPlaying, interval: this.interval };
  }
  
  // 可选间隔时间(秒)
  static readonly INTERVAL_OPTIONS = [
    { label: '3秒', value: 3000 },
    { label: '5秒', value: 5000 },
    { label: '10秒', value: 10000 },
    { label: '15秒', value: 15000 },
    { label: '30秒', value: 30000 },
    { label: '60秒', value: 60000 },
  ];
}

六、UI交互设计

6.1 名言展示卡片

@Component
struct QuoteCard {
  @Link quote: Quote | null;
  @Link isFavorite: boolean;
  @State fontSize: number = 20;
  @State textOpacity: number = 1;
  
  onToggleFavorite: (() => void) | null = null;
  
  build() {
    Column() {
      if (this.quote) {
        // 名言正文
        Text(this.quote.text)
          .fontSize(this.fontSize)
          .fontWeight(FontWeight.Medium)
          .fontColor('#333333')
          .lineHeight(this.fontSize * 1.8)
          .textAlign(TextAlign.Center)
          .opacity(this.textOpacity)
          .padding({ left: 24, right: 24, top: 32, bottom: 16 })
        
        Divider()
          .width('60%')
          .height(2)
          .color('#E0E0E0')
          .margin({ bottom: 12 })
        
        // 作者
        Text(`—— ${this.quote.author}`)
          .fontSize(16)
          .fontColor('#666666')
          .textAlign(TextAlign.Right)
          .width('100%')
          .padding({ right: 24 })
        
        // 标签
        if (this.quote.tags.length > 0) {
          Row({ space: 8 }) {
            ForEach(this.quote.tags, (tag: string) => {
              Text(`#${tag}`)
                .fontSize(12)
                .fontColor('#999999')
                .backgroundColor('#F5F5F5')
                .padding({ left: 8, right: 8, top: 2, bottom: 2 })
                .borderRadius(4)
            })
          }
          .width('100%')
          .padding({ left: 24, right: 24, top: 8 })
          .flexWrap(FlexWrap.Wrap)
        }
        
        // 操作按钮
        Row() {
          // 收藏按钮
          Button() {
            Text(this.isFavorite ? '❤️' : '🤍')
              .fontSize(24)
          }
          .backgroundColor('transparent')
          .onClick(() => {
            this.onToggleFavorite?.();
          })
          
          // 分享按钮
          Button() {
            Text('📤')
              .fontSize(24)
          }
          .backgroundColor('transparent')
          .onClick(() => {
            this.shareQuote();
          })
          
          // 字体大小调整
          Button() {
            Text('A')
              .fontSize(18)
              .fontWeight(FontWeight.Bold)
          }
          .backgroundColor('transparent')
          .onClick(() => {
            this.fontSize = Math.min(36, this.fontSize + 2);
          })
          
          Button() {
            Text('A')
              .fontSize(14)
              .fontWeight(FontWeight.Bold)
          }
          .backgroundColor('transparent')
          .onClick(() => {
            this.fontSize = Math.max(14, this.fontSize - 2);
          })
        }
        .width('100%')
        .justifyContent(FlexAlign.Center)
        .padding(16)
        .margin({ top: 8 })
      } else {
        Text('暂无名言')
          .fontSize(18)
          .fontColor('#999999')
      }
    }
    .width('100%')
    .backgroundColor('#FFFFFF')
    .borderRadius(16)
    .shadow({ radius: 8, color: '#20000000', offsetY: 4 })
  }
  
  private shareQuote(): void {
    if (!this.quote) return;
    const shareText = `"${this.quote.text}"\n—— ${this.quote.author}\n\n—— 来自 名言警句 App`;
    // 复制到剪贴板
    const pasteboard = new Pasteboard();
    pasteboard.setData(shareText);
  }
}

6.2 分类筛选栏

@Component
struct CategoryFilterBar {
  @Link categories: QuoteCategory[];
  @Link selectedCategory: string;
  onCategoryChange: ((categoryId: string) => void) | null = null;
  
  build() {
    Scroll({ direction: ScrollDirection.Horizontal }) {
      Row({ space: 8 }) {
        ForEach(this.categories, (cat: QuoteCategory) => {
          Column() {
            Text(cat.icon)
              .fontSize(20)
            Text(cat.name)
              .fontSize(12)
              .fontColor(this.selectedCategory === cat.id ? '#4CAF50' : '#666666')
            Text(`(${cat.count})`)
              .fontSize(10)
              .fontColor('#999999')
          }
          .padding({ left: 16, right: 16, top: 8, bottom: 8 })
          .backgroundColor(this.selectedCategory === cat.id ? '#E8F5E9' : '#F5F5F5')
          .borderRadius(20)
          .onClick(() => {
            this.onCategoryChange?.(cat.id);
          })
        })
      }
      .padding({ left: 16, right: 16 })
    }
    .width('100%')
    .height(80)
  }
}

七、自动轮播UI

@Component
struct AutoPlayControl {
  @Link isPlaying: boolean;
  @Link interval: number;
  onToggle: (() => void) | null = null;
  onIntervalChange: ((interval: number) => void) | null = null;
  @State showIntervalPicker: boolean = false;
  
  build() {
    Row() {
      // 播放/暂停按钮
      Button() {
        Text(this.isPlaying ? '⏸ 暂停' : '▶️ 自动播放')
          .fontSize(14)
      }
      .backgroundColor(this.isPlaying ? '#FF9800' : '#4CAF50')
      .fontColor('#FFFFFF')
      .borderRadius(16)
      .onClick(() => {
        this.onToggle?.();
      })
      
      // 间隔设置
      Button(`${(this.interval / 1000).toFixed(0)}s`)
        .backgroundColor('#E0E0E0')
        .fontColor('#333333')
        .borderRadius(16)
        .margin({ left: 8 })
        .onClick(() => {
          this.showIntervalPicker = !this.showIntervalPicker;
        })
    }
    .width('100%')
    .justifyContent(FlexAlign.Center)
    .padding(16)
  }
}

八、性能优化

8.1 搜索优化

对于搜索功能,使用防抖(Debounce)技术避免频繁过滤:

class DebounceSearch {
  private timer: number | null = null;
  private delay: number = 300;
  private callback: ((query: string) => void) | null = null;
  
  setCallback(callback: (query: string) => void): void {
    this.callback = callback;
  }
  
  search(query: string): void {
    if (this.timer !== null) {
      clearTimeout(this.timer);
    }
    this.timer = setTimeout(() => {
      this.callback?.(query);
      this.timer = null;
    }, this.delay);
  }
  
  destroy(): void {
    if (this.timer !== null) {
      clearTimeout(this.timer);
      this.timer = null;
    }
  }
}

8.2 数据缓存

class QuoteCache {
  private static cache = new Map<string, { data: any; timestamp: number }>();
  private static readonly TTL = 5 * 60 * 1000; // 5分钟
  
  static get<T>(key: string): T | null {
    const cached = this.cache.get(key);
    if (cached && Date.now() - cached.timestamp < this.TTL) {
      return cached.data as T;
    }
    return null;
  }
  
  static set<T>(key: string, data: T): void {
    this.cache.set(key, { data, timestamp: Date.now() });
  }
  
  static invalidate(pattern?: string): void {
    if (pattern) {
      for (const key of this.cache.keys()) {
        if (key.startsWith(pattern)) {
          this.cache.delete(key);
        }
      }
    } else {
      this.cache.clear();
    }
  }
}

九、测试策略

9.1 单元测试

describe('RandomQuoteSelector', () => {
  const selector = new RandomQuoteSelector();
  const quotes = [
    { id: '1', text: '名言1', author: '作者1', category: '人生', tags: [], popularity: 5 },
    { id: '2', text: '名言2', author: '作者2', category: '励志', tags: [], popularity: 3 },
    { id: '3', text: '名言3', author: '作者3', category: '哲理', tags: [], popularity: 4 },
  ];
  
  it('should select a random quote', () => {
    const result = selector.selectRandom(quotes);
    expect(result).not.toBeNull();
    expect(quotes).toContain(result);
  });
  
  it('should return null for empty array', () => {
    expect(selector.selectRandom([])).toBeNull();
  });
  
  it('should not repeat recently shown quotes', () => {
    const results = new Set<string>();
    for (let i = 0; i < 10; i++) {
      const quote = selector.selectRandomWithHistory(quotes);
      if (quote) results.add(quote.id);
    }
    // 在有历史记录的情况下,应该能覆盖所有名言
    expect(results.size).toBeGreaterThanOrEqual(1);
  });
  
  it('should select daily quote based on date', () => {
    const today = selector.selectDailyQuote(quotes);
    const tomorrow = selector.selectDailyQuote(quotes);
    // 同一天应该返回相同结果
    expect(today?.id).toBe(tomorrow?.id);
  });
});
describe('QuoteFilterEngine', () => {
  const engine = new QuoteFilterEngine();
  const quotes = [
    { id: '1', text: '生活名言', author: '老子', category: '人生', tags: ['生活'], popularity: 5 },
    { id: '2', text: '励志名言', author: '孔子', category: '励志', tags: ['奋斗'], popularity: 4 },
  ];
  
  it('should filter by category', () => {
    engine.setCategoryFilter('人生');
    const result = engine.filter(quotes);
    expect(result.length).toBe(1);
    expect(result[0].id).toBe('1');
  });
  
  it('should filter by search query', () => {
    engine.setSearchQuery('励志');
    const result = engine.filter(quotes);
    expect(result.length).toBe(1);
  });
});

十、总结

10.1 核心技术要点

  1. 结构化数据管理:设计多维度的名言数据结构,支持分类、标签、作者、出处等多维度检索。
  2. 随机算法体系:实现多种随机策略——完全随机、带历史避免重复的随机、每日固定种子伪随机、加权随机。
  3. 分类过滤引擎:支持多维度的组合过滤,包括分类、搜索、作者等条件的灵活组合。
  4. 收藏管理器:基于Set数据结构的收藏管理,配合Preferences API实现持久化存储。
  5. 自动轮播系统:基于setInterval的定时切换,支持可调节的间隔时间。
  6. 防抖搜索:使用Debounce技术优化搜索性能,避免频繁过滤。

10.2 扩展方向

  1. 在线名言库:接入网络API获取更多名言,支持用户上传名言。
  2. 每日推送通知:定时推送名言通知到通知栏。
  3. 名言壁纸生成:将名言转换为精美的壁纸图片。
  4. AI生成名言:基于大模型自动生成新的名言。
  5. 社交功能:用户之间分享名言、评论、点赞。
  6. 多语言支持:名言原文和翻译的显示。

10.3 核心代码量统计

模块 核心代码行数 接口数 组件数
名言数据库 200 6 -
随机选择算法 120 5 -
分类过滤引擎 100 6 -
收藏管理器 80 6 -
轮播控制器 70 5 -
防抖搜索 40 3 -
UI组件 350 4 6
测试用例 80 - -
总计 1040 35 6
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐