龍魂 · 鸿蒙 ArkTS 实战:Team Birthday Reminder

核心定位

维度 说明
平台 鸿蒙 HarmonyOS NEXT · 纯血鸿蒙
语言 ArkTS · 声明式UI
架构 龍魂蚁群触角 → 鸿蒙原生能力
场景 团队生日提醒 · 办公交互
主权 数据本地 · 国密可选 · 不上传云端

系统架构

┌─────────────────────────────────────────┐
│           龍魂系统 · 鸿蒙适配层            │
│  DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️ │
│  UID: 9622                              │
├─────────────────────────────────────────┤
│         鸿蒙 ArkTS 应用层                 │
│                                         │
│  EntryAbility → BirthdayReminderPage     │
│  ├── 状态建模(AppStorage/LocalStorage)   │
│  ├── 团队数据管理(增删改查)              │
│  ├── 生日计算引擎(农历/公历)             │
│  ├── 通知提醒系统(后台代理)              │
│  └── 办公交互(分享/导出/日历同步)         │
├─────────────────────────────────────────┤
│         鸿蒙系统能力层                      │
│                                         │
│  日历(Calendar) · 联系人(Contacts)      │
│  通知(Notification) · 后台任务(WorkScheduler) │
│  数据存储(RelationalStore) · 文件(File)    │
│  网络(Http) · 分享(Share)               │
└─────────────────────────────────────────┘

状态建模核心

// entry/src/main/ets/models/BirthdayModel.ets
// 龍魂 · 生日数据模型 · ArkTS

// === DNA常量 ===
const MASTER_DNA = "ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️";
const MASTER_UID = "9622";
const CONFIRM_SEAL = "#CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z";

// === 人员档案 ===
export class TeamMember {
  id: string;           // 唯一ID · 哈希生成
  name: string;         // 姓名
  avatar: Resource | string; // 头像路径
  birthdayType: 'solar' | 'lunar'; // 公历/农历
  birthdayMonth: number;  // 月
  birthdayDay: number;    // 日
  birthYear?: number;      // 年(可选,算年龄)
  department: string;     // 部门
  role: string;            // 职位
  phone?: string;          // 电话
  email?: string;          // 邮箱
  tags: string[];         // 标签
  dnaSignature: string;   // 数据签名
  
  constructor(data: Partial<TeamMember>) {
    this.id = data.id || this.generateId(data.name || '', data.birthdayMonth || 0, data.birthdayDay || 0);
    this.name = data.name || '';
    this.avatar = data.avatar || $r('app.media.default_avatar');
    this.birthdayType = data.birthdayType || 'solar';
    this.birthdayMonth = data.birthdayMonth || 1;
    this.birthdayDay = data.birthdayDay || 1;
    this.birthYear = data.birthYear;
    this.department = data.department || '未分配';
    this.role = data.role || '成员';
    this.phone = data.phone;
    this.email = data.email;
    this.tags = data.tags || [];
    this.dnaSignature = this.signData();
  }
  
  private generateId(name: string, month: number, day: number): string {
    // 龍魂ID生成:姓名+月日+DNA种子
    const seed = `${MASTER_DNA}-${name}-${month}-${day}-${Date.now()}`;
    // 简化:实际用鸿蒙 crypto API
    return `LH-${MASTER_UID}-${month.toString().padStart(2,'0')}${day.toString().padStart(2,'0')}-${Math.random().toString(36).substr(2,6)}`;
  }
  
  private signData(): string {
    // 数据签名:防篡改
    const payload = `${this.id}-${this.name}-${this.birthdayMonth}-${this.birthdayDay}`;
    // 简化:实际用 SM3 国密
    return `SM3-${payload.slice(0,16)}`;
  }
  
  // 计算下一个生日
  getNextBirthday(): Date {
    const now = new Date();
    const currentYear = now.getFullYear();
    
    if (this.birthdayType === 'solar') {
      // 公历直接算
      let next = new Date(currentYear, this.birthdayMonth - 1, this.birthdayDay);
      if (next < now) {
        next = new Date(currentYear + 1, this.birthdayMonth - 1, this.birthdayDay);
      }
      return next;
    } else {
      // 农历转换(需农历库)
      // 简化:调用鸿蒙日历API或本地农历表
      return this.getLunarNextBirthday(currentYear);
    }
  }
  
  private getLunarNextBirthday(year: number): Date {
    // 农历生日转公历(占位)
    // 实际:查农历转换表或使用鸿蒙日历能力
    const lunarMap: Record<string, string> = {
      '1-1': '2026-02-17',  // 正月初一
      '5-5': '2026-06-19',  // 五月初五
      '8-15': '2026-09-25', // 八月十五
      // ... 完整农历表
    };
    const key = `${this.birthdayMonth}-${this.birthdayDay}`;
    const solarStr = lunarMap[key] || `${year}-${this.birthdayMonth.toString().padStart(2,'0')}-${this.birthdayDay.toString().padStart(2,'0')}`;
    return new Date(solarStr);
  }
  
  // 计算年龄
  getAge(): number {
    if (!this.birthYear) return 0;
    const now = new Date();
    let age = now.getFullYear() - this.birthYear;
    const birthdayThisYear = new Date(now.getFullYear(), this.birthdayMonth - 1, this.birthdayDay);
    if (now < birthdayThisYear) {
      age--;
    }
    return age;
  }
  
  // 获取生日倒计时天数
  getDaysUntilBirthday(): number {
    const next = this.getNextBirthday();
    const now = new Date();
    const diff = next.getTime() - now.getTime();
    return Math.ceil(diff / (1000 * 60 * 60 * 24));
  }
  
  // 是否今天生日
  isBirthdayToday(): boolean {
    const now = new Date();
    return now.getMonth() === this.birthdayMonth - 1 && now.getDate() === this.birthdayDay;
  }
}

// === 团队数据状态 ===
@Observed
export class TeamState {
  members: TeamMember[] = [];
  filterDepartment: string = '全部';
  filterTag: string = '';
  sortBy: 'date' | 'name' | 'department' = 'date';
  searchQuery: string = '';
  
  // 派生数据:过滤后的成员
  get filteredMembers(): TeamMember[] {
    let result = this.members;
    
    // 部门过滤
    if (this.filterDepartment !== '全部') {
      result = result.filter(m => m.department === this.filterDepartment);
    }
    
    // 标签过滤
    if (this.filterTag) {
      result = result.filter(m => m.tags.includes(this.filterTag));
    }
    
    // 搜索过滤
    if (this.searchQuery) {
      const q = this.searchQuery.toLowerCase();
      result = result.filter(m => 
        m.name.toLowerCase().includes(q) ||
        m.department.toLowerCase().includes(q) ||
        m.role.toLowerCase().includes(q)
      );
    }
    
    // 排序
    switch (this.sortBy) {
      case 'date':
        result.sort((a, b) => a.getDaysUntilBirthday() - b.getDaysUntilBirthday());
        break;
      case 'name':
        result.sort((a, b) => a.name.localeCompare(b.name, 'zh-CN'));
        break;
      case 'department':
        result.sort((a, b) => a.department.localeCompare(b.department, 'zh-CN'));
        break;
    }
    
    return result;
  }
  
  // 即将过生日的(7天内)
  get upcomingBirthdays(): TeamMember[] {
    return this.members.filter(m => m.getDaysUntilBirthday() <= 7);
  }
  
  // 今天过生日的
  get todayBirthdays(): TeamMember[] {
    return this.members.filter(m => m.isBirthdayToday());
  }
  
  // 部门列表
  get departments(): string[] {
    const deps = new Set(this.members.map(m => m.department));
    return ['全部', ...Array.from(deps).sort()];
  }
  
  // 所有标签
  get allTags(): string[] {
    const tags = new Set<string>();
    this.members.forEach(m => m.tags.forEach(t => tags.add(t)));
    return Array.from(tags).sort();
  }
  
  // 添加成员
  addMember(member: TeamMember): void {
    this.members.push(member);
    this.persist();
  }
  
  // 删除成员
  removeMember(id: string): void {
    const idx = this.members.findIndex(m => m.id === id);
    if (idx >= 0) {
      this.members.splice(idx, 1);
      this.persist();
    }
  }
  
  // 更新成员
  updateMember(id: string, data: Partial<TeamMember>): void {
    const idx = this.members.findIndex(m => m.id === id);
    if (idx >= 0) {
      const old = this.members[idx];
      this.members[idx] = new TeamMember({ ...old, ...data, id: old.id });
      this.persist();
    }
  }
  
  // 持久化到本地数据库
  private persist(): void {
    // 调用 RelationalStore 存储
    AppStorage.setOrCreate('team_members', JSON.stringify(this.members));
    // 实际:写入鸿蒙关系型数据库
  }
  
  // 从本地加载
  load(): void {
    const stored = AppStorage.get<string>('team_members');
    if (stored) {
      try {
        const parsed = JSON.parse(stored);
        this.members = parsed.map((m: any) => new TeamMember(m));
      } catch (e) {
        console.error('加载团队数据失败', e);
      }
    }
  }
}

// === 全局状态单例 ===
export const teamState = new TeamState();
AppStorage.setOrCreate('teamState', teamState);

主页面完整实现

// entry/src/main/ets/pages/BirthdayReminderPage.ets
// 龍魂 · 生日提醒主页面 · ArkTS

import { TeamMember, TeamState, teamState } from '../models/BirthdayModel';
import { BirthdayDetailDialog } from '../components/BirthdayDetailDialog';
import { MemberEditDialog } from '../components/MemberEditDialog';
import { NotificationManager } from '../managers/NotificationManager';

@Entry
@Component
struct BirthdayReminderPage {
  @StorageLink('teamState') teamState: TeamState = teamState;
  @State private showAddDialog: boolean = false;
  @State private editingMember: TeamMember | null = null;
  @State private selectedTab: number = 0;
  @State private showSearch: boolean = false;
  @State private searchText: string = '';
  
  // 页面加载
  aboutToAppear() {
    this.teamState.load();
    NotificationManager.requestPermission();
    NotificationManager.scheduleBirthdayReminders(this.teamState.members);
  }
  
  // 构建UI
  build() {
    Column() {
      // === 顶部标题栏 ===
      this.HeaderBuilder()
      
      // === 今日生日特别提醒 ===
      if (this.teamState.todayBirthdays.length > 0) {
        this.TodayBirthdayBanner()
      }
      
      // === 标签切换 ===
      this.TabBuilder()
      
      // === 搜索栏(条件显示) ===
      if (this.showSearch) {
        this.SearchBuilder()
      }
      
      // === 过滤工具栏 ===
      this.FilterBuilder()
      
      // === 成员列表 ===
      this.MemberListBuilder()
      
      // === 底部操作栏 ===
      this.FooterBuilder()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#0a0a0a')  // 龍魂深色主题
  }
  
  // === 标题栏 ===
  @Builder
  HeaderBuilder() {
    Row() {
      // 龍魂Logo
      Text('🐉')
        .fontSize(28)
        .margin({ right: 8 })
      
      Column() {
        Text('龍魂生日提醒')
          .fontSize(20)
          .fontWeight(FontWeight.Bold)
          .fontColor('#c41e3a')  // 龍魂红
        
        Text(`UID:${MASTER_UID} · ${this.teamState.members.length}`)
          .fontSize(12)
          .fontColor('#666')
      }
      .alignItems(HorizontalAlign.Start)
      
      Blank()
      
      // 搜索按钮
      Button() {
        Text(this.showSearch ? '✕' : '🔍')
          .fontSize(20)
      }
      .type(ButtonType.Circle)
      .backgroundColor('#1a1a1a')
      .width(40)
      .height(40)
      .onClick(() => {
        this.showSearch = !this.showSearch;
        if (!this.showSearch) {
          this.teamState.searchQuery = '';
        }
      })
      
      // 添加按钮
      Button() {
        Text('+')
          .fontSize(24)
          .fontColor('#fff')
      }
      .type(ButtonType.Circle)
      .backgroundColor('#c41e3a')
      .width(40)
      .height(40)
      .onClick(() => {
        this.editingMember = null;
        this.showAddDialog = true;
      })
    }
    .width('100%')
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#1a1a1a')
    .border({ width: { bottom: 1 }, color: '#333' })
  }
  
  // === 今日生日横幅 ===
  @Builder
  TodayBirthdayBanner() {
    Column() {
      Row() {
        Text('🎂')
          .fontSize(32)
          .margin({ right: 12 })
        
        Column() {
          Text(`今天 ${this.teamState.todayBirthdays.length} 位同学生日`)
            .fontSize(16)
            .fontWeight(FontWeight.Bold)
            .fontColor('#fff')
          
          Text(this.teamState.todayBirthdays.map(m => m.name).join('、'))
            .fontSize(14)
            .fontColor('#ffcc00')
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
        }
        .alignItems(HorizontalAlign.Start)
        .layoutWeight(1)
        
        Button('祝福')
          .fontSize(14)
          .fontColor('#fff')
          .backgroundColor('#c41e3a')
          .borderRadius(16)
          .padding({ left: 16, right: 16 })
          .onClick(() => {
            this.sendBlessings(this.teamState.todayBirthdays);
          })
      }
      .width('100%')
      .padding(16)
    }
    .width('100%')
    .backgroundColor('linear-gradient(90deg, #2a1a1a 0%, #1a0a0a 100%)')
    .border({ width: { bottom: 1 }, color: '#c41e3a' })
  }
  
  // === 标签切换 ===
  @Builder
  TabBuilder() {
    Row() {
      ForEach([
        { label: '全部', count: this.teamState.members.length },
        { label: '7天内', count: this.teamState.upcomingBirthdays.length },
        { label: '今天', count: this.teamState.todayBirthdays.length }
      ], (item: {label: string, count: number}, index: number) => {
        Column() {
          Text(`${item.label} ${item.count > 0 ? `(${item.count})` : ''}`)
            .fontSize(14)
            .fontWeight(this.selectedTab === index ? FontWeight.Bold : FontWeight.Normal)
            .fontColor(this.selectedTab === index ? '#c41e3a' : '#888')
          
          // 下划线指示器
          if (this.selectedTab === index) {
            Divider()
              .strokeWidth(2)
              .color('#c41e3a')
              .width(20)
              .margin({ top: 4 })
          }
        }
        .padding({ top: 12, bottom: 12, left: 16, right: 16 })
        .onClick(() => {
          this.selectedTab = index;
          // 更新过滤条件
          switch (index) {
            case 1: // 7天内
              // 实际:过滤逻辑
              break;
            case 2: // 今天
              // 实际:过滤逻辑
              break;
          }
        })
      })
    }
    .width('100%')
    .justifyContent(FlexAlign.SpaceAround)
    .backgroundColor('#1a1a1a')
    .border({ width: { bottom: 1 }, color: '#333' })
  }
  
  // === 搜索栏 ===
  @Builder
  SearchBuilder() {
    Row() {
      TextInput({ placeholder: '搜索姓名、部门、职位...', text: $$this.searchText })
        .width('100%')
        .height(40)
        .backgroundColor('#2a2a2a')
        .fontColor('#fff')
        .placeholderColor('#666')
        .borderRadius(8)
        .padding({ left: 12, right: 12 })
        .onChange((value: string) => {
          this.teamState.searchQuery = value;
        })
    }
    .width('100%')
    .padding({ left: 16, right: 16, top: 8, bottom: 8 })
    .backgroundColor('#1a1a1a')
  }
  
  // === 过滤工具栏 ===
  @Builder
  FilterBuilder() {
    Row() {
      // 部门选择
      Select(this.teamState.departments.map(dep => ({ value: dep })))
        .selected(this.teamState.departments.indexOf(this.teamState.filterDepartment))
        .fontColor('#ccc')
        .backgroundColor('#2a2a2a')
        .borderRadius(8)
        .padding({ left: 8, right: 8 })
        .onSelect((index: number) => {
          this.teamState.filterDepartment = this.teamState.departments[index];
        })
      
      Blank()
      
      // 排序选择
      Select([
        { value: '按日期' },
        { value: '按姓名' },
        { value: '按部门' }
      ])
        .selected(['date', 'name', 'department'].indexOf(this.teamState.sortBy))
        .fontColor('#ccc')
        .backgroundColor('#2a2a2a')
        .borderRadius(8)
        .padding({ left: 8, right: 8 })
        .onSelect((index: number) => {
          const sorts: ('date' | 'name' | 'department')[] = ['date', 'name', 'department'];
          this.teamState.sortBy = sorts[index];
        })
    }
    .width('100%')
    .height(48)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#1a1a1a')
    .border({ width: { bottom: 1 }, color: '#333' })
  }
  
  // === 成员列表 ===
  @Builder
  MemberListBuilder() {
    List() {
      ForEach(this.teamState.filteredMembers, (member: TeamMember) => {
        ListItem() {
          this.MemberCardBuilder(member)
        }
        .swipeAction({ end: this.DeleteActionBuilder(member) })
        .onClick(() => {
          this.editingMember = member;
          this.showAddDialog = true;
        })
      }, (member: TeamMember) => member.id)
    }
    .width('100%')
    .layoutWeight(1)
    .divider({ strokeWidth: 1, color: '#222' })
    .scrollBar(BarState.Auto)
    .edgeEffect(EdgeEffect.Spring)
    .padding({ bottom: 16 })
  }
  
  // === 成员卡片 ===
  @Builder
  MemberCardBuilder(member: TeamMember) {
    Row() {
      // 头像
      Stack() {
        Image(member.avatar)
          .width(48)
          .height(48)
          .borderRadius(24)
          .objectFit(ImageFit.Cover)
        
        // 生日标识
        if (member.isBirthdayToday()) {
          Text('🎂')
            .fontSize(16)
            .position({ x: 32, y: -4 })
        }
      }
      .width(48)
      .height(48)
      .margin({ right: 12 })
      
      // 信息
      Column() {
        Row() {
          Text(member.name)
            .fontSize(16)
            .fontWeight(FontWeight.Medium)
            .fontColor('#fff')
          
          // 年龄标签
          if (member.getAge() > 0) {
            Text(`${member.getAge()}`)
              .fontSize(12)
              .fontColor('#888')
              .backgroundColor('#2a2a2a')
              .borderRadius(4)
              .padding({ left: 6, right: 6 })
              .margin({ left: 8 })
          }
          
          Blank()
          
          // 倒计时
          const days = member.getDaysUntilBirthday();
          Text(days === 0 ? '今天!' : `${days}天后`)
            .fontSize(12)
            .fontColor(days <= 7 ? '#c41e3a' : '#888')
            .fontWeight(days <= 7 ? FontWeight.Bold : FontWeight.Normal)
        }
        .width('100%')
        
        Row() {
          Text(`${member.department} · ${member.role}`)
            .fontSize(13)
            .fontColor('#888')
            .maxLines(1)
            .textOverflow({ overflow: TextOverflow.Ellipsis })
            .layoutWeight(1)
          
          // 农历标识
          if (member.birthdayType === 'lunar') {
            Text('农历')
              .fontSize(11)
              .fontColor('#ffcc00')
              .backgroundColor('rgba(255,204,0,0.1)')
              .borderRadius(4)
              .padding({ left: 4, right: 4 })
          }
        }
        .width('100%')
        .margin({ top: 4 })
        
        // 标签
        Row() {
          ForEach(member.tags.slice(0, 3), (tag: string) => {
            Text(tag)
              .fontSize(11)
              .fontColor('#aaa')
              .backgroundColor('#2a2a2a')
              .borderRadius(4)
              .padding({ left: 6, right: 6 })
              .margin({ right: 4 })
          })
        }
        .width('100%')
        .margin({ top: 6 })
      }
      .layoutWeight(1)
      .alignItems(HorizontalAlign.Start)
    }
    .width('100%')
    .padding(16)
    .backgroundColor(member.isBirthdayToday() ? 'rgba(196,30,58,0.1)' : '#0a0a0a')
    .borderRadius(8)
    .margin({ left: 12, right: 12, top: 8 })
  }
  
  // === 删除操作 ===
  @Builder
  DeleteActionBuilder(member: TeamMember) {
    Row() {
      Button() {
        Text('删除')
          .fontColor('#fff')
          .fontSize(14)
      }
      .type(ButtonType.Capsule)
      .backgroundColor('#ff0000')
      .width(80)
      .height('100%')
      .onClick(() => {
        AlertDialog.show({
          title: '确认删除',
          message: `确定删除 ${member.name} 吗?`,
          primaryButton: {
            value: '删除',
            action: () => {
              this.teamState.removeMember(member.id);
            }
          },
          secondaryButton: {
            value: '取消',
            action: () => {}
          }
        });
      })
    }
    .width(80)
    .height('100%')
    .padding({ left: 4 })
  }
  
  // === 底部操作栏 ===
  @Builder
  FooterBuilder() {
    Row() {
      Button('导入')
        .fontSize(14)
        .fontColor('#ccc')
        .backgroundColor('#1a1a1a')
        .border({ width: 1, color: '#444' })
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.importMembers();
        })
      
      Button('导出')
        .fontSize(14)
        .fontColor('#ccc')
        .backgroundColor('#1a1a1a')
        .border({ width: 1, color: '#444' })
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.exportMembers();
        })
      
      Button('分享今日')
        .fontSize(14)
        .fontColor('#fff')
        .backgroundColor('#c41e3a')
        .borderRadius(8)
        .padding({ left: 16, right: 16 })
        .onClick(() => {
          this.shareTodayBirthdays();
        })
    }
    .width('100%')
    .height(56)
    .padding({ left: 16, right: 16 })
    .backgroundColor('#1a1a1a')
    .border({ width: { top: 1 }, color: '#333' })
    .justifyContent(FlexAlign.SpaceAround)
  }
  
  // === 功能方法 ===
  private sendBlessings(members: TeamMember[]) {
    // 批量发送祝福(短信/邮件/企业微信)
    members.forEach(m => {
      NotificationManager.sendBlessing(m);
    });
    AlertDialog.show({
      title: '祝福已发送',
      message: `已向 ${members.length} 位同学发送生日祝福`,
      confirm: { value: '好的', action: () => {} }
    });
  }
  
  private importMembers() {
    // 从Excel/CSV导入
    // 调用鸿蒙文件选择器
    // 解析数据并批量添加
  }
  
  private exportMembers() {
    // 导出为Excel/CSV
    const data = this.teamState.members.map(m => ({
      姓名: m.name,
      部门: m.department,
      职位: m.role,
      生日类型: m.birthdayType === 'solar' ? '公历' : '农历',
      生日: `${m.birthdayMonth}${m.birthdayDay}`,
      年龄: m.getAge(),
      电话: m.phone || '',
      邮箱: m.email || ''
    }));
    // 生成CSV并分享
  }
  
  private shareTodayBirthdays() {
    // 分享今日生日列表
    const today = this.teamState.todayBirthdays;
    if (today.length === 0) {
      AlertDialog.show({
        title: '今天没有生日',
        message: '看看7天内过生日的同学吧',
        confirm: { value: '好的', action: () => {} }
      });
      return;
    }
    
    const text = `🎂 今天过生日的同学:\n${today.map(m => `${m.name} (${m.department})`).join('\n')}\n\n来自龍魂生日提醒 UID9622`;
    // 调用鸿蒙分享API
  }
}

通知管理器

// entry/src/main/ets/managers/NotificationManager.ets
// 龍魂 · 通知管理器 · ArkTS

import { notificationManager } from '@kit.NotificationKit';
import { wantAgent } from '@kit.AbilityKit';
import { TeamMember } from '../models/BirthdayModel';

export class NotificationManager {
  static async requestPermission(): Promise<boolean> {
    try {
      // 请求通知权限
      const granted = await notificationManager.requestEnableNotification();
      return granted;
    } catch (err) {
      console.error('请求通知权限失败', err);
      return false;
    }
  }
  
  static scheduleBirthdayReminders(members: TeamMember[]): void {
    // 取消旧提醒
    notificationManager.cancelAllNotifications();
    
    // 为每个成员设置提醒
    members.forEach(member => {
      const nextBirthday = member.getNextBirthday();
      const remindDate = new Date(nextBirthday);
      remindDate.setDate(remindDate.getDate() - 1); // 提前一天提醒
      remindDate.setHours(9, 0, 0, 0); // 上午9点
      
      // 设置定时通知
      this.scheduleNotification(
        `birthday-${member.id}`,
        `🎂 ${member.name} 明天过生日`,
        `${member.department}${member.name}明天${member.birthdayMonth}${member.birthdayDay}日过生日,记得送上祝福!`,
        remindDate.getTime()
      );
      
      // 当天提醒
      const todayRemind = new Date(nextBirthday);
      todayRemind.setHours(9, 0, 0, 0);
      this.scheduleNotification(
        `birthday-today-${member.id}`,
        `🎂 今天 ${member.name} 过生日!`,
        `快给${member.name}送上生日祝福吧!`,
        todayRemind.getTime()
      );
    });
  }
  
  static async scheduleNotification(id: string, title: string, content: string, triggerTime: number): Promise<void> {
    // 使用鸿蒙后台任务或闹钟能力
    // 简化:实际使用 WorkScheduler 或 AlarmManager
    try {
      const notificationRequest: notificationManager.NotificationRequest = {
        id: parseInt(id.replace(/\D/g, '').slice(0, 8)) || 1,
        content: {
          notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
          normal: {
            title: title,
            text: content,
            additionalText: '龍魂生日提醒 UID9622'
          }
        },
        notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION,
        deliveryTime: triggerTime,
        tapDismissed: true
      };
      
      await notificationManager.publish(notificationRequest);
    } catch (err) {
      console.error('设置通知失败', err);
    }
  }
  
  static sendBlessing(member: TeamMember): void {
    // 发送祝福(占位:实际调用短信/邮件/企业微信API)
    console.log(`${member.name} 发送生日祝福`);
    // 实际:调用鸿蒙短信能力或企业微信SDK
  }
}

数据持久化

// entry/src/main/ets/database/BirthdayDatabase.ets
// 龍魂 · 关系型数据库 · ArkTS

import { relationalStore } from '@kit.ArkData';

const STORE_NAME = 'LongHunBirthday.db';
const TABLE_NAME = 'team_members';

export class BirthdayDatabase {
  private store: relationalStore.RdbStore | null = null;
  
  async init(): Promise<void> {
    const config: relationalStore.StoreConfig = {
      name: STORE_NAME,
      securityLevel: relationalStore.SecurityLevel.S1, // 本地加密
      encrypt: true  // 启用加密
    };
    
    this.store = await relationalStore.getRdbStore(getContext(), config);
    
    // 创建表
    await this.store.executeSql(`
      CREATE TABLE IF NOT EXISTS ${TABLE_NAME} (
        id TEXT PRIMARY KEY,
        name TEXT NOT NULL,
        avatar TEXT,
        birthday_type TEXT,
        birthday_month INTEGER,
        birthday_day INTEGER,
        birth_year INTEGER,
        department TEXT,
        role TEXT,
        phone TEXT,
        email TEXT,
        tags TEXT,
        dna_signature TEXT,
        created_at INTEGER,
        updated_at INTEGER
      )
    `);
  }
  
  async insert(member: any): Promise<number> {
    if (!this.store) return -1;
    
    const values = new relationalStore.ValuesBucket();
    values.putString('id', member.id);
    values.putString('name', member.name);
    values.putString('avatar', member.avatar);
    values.putString('birthday_type', member.birthdayType);
    values.putInt('birthday_month', member.birthdayMonth);
    values.putInt('birthday_day', member.birthdayDay);
    values.putInt('birth_year', member.birthYear || 0);
    values.putString('department', member.department);
    values.putString('role', member.role);
    values.putString('phone', member.phone || '');
    values.putString('email', member.email || '');
    values.putString('tags', JSON.stringify(member.tags));
    values.putString('dna_signature', member.dnaSignature);
    values.putLong('created_at', Date.now());
    values.putLong('updated_at', Date.now());
    
    return await this.store.insert(TABLE_NAME, values);
  }
  
  async queryAll(): Promise<any[]> {
    if (!this.store) return [];
    
    const predicates = new relationalStore.RdbPredicates(TABLE_NAME);
    const resultSet = await this.store.query(predicates, [
      'id', 'name', 'avatar', 'birthday_type', 'birthday_month', 'birthday_day',
      'birth_year', 'department', 'role', 'phone', 'email', 'tags', 'dna_signature'
    ]);
    
    const members: any[] = [];
    while (resultSet.goToNextRow()) {
      members.push({
        id: resultSet.getString(resultSet.getColumnIndex('id')),
        name: resultSet.getString(resultSet.getColumnIndex('name')),
        avatar: resultSet.getString(resultSet.getColumnIndex('avatar')),
        birthdayType: resultSet.getString(resultSet.getColumnIndex('birthday_type')),
        birthdayMonth: resultSet.getLong(resultSet.getColumnIndex('birthday_month')),
        birthdayDay: resultSet.getLong(resultSet.getColumnIndex('birthday_day')),
        birthYear: resultSet.getLong(resultSet.getColumnIndex('birth_year')),
        department: resultSet.getString(resultSet.getColumnIndex('department')),
        role: resultSet.getString(resultSet.getColumnIndex('role')),
        phone: resultSet.getString(resultSet.getColumnIndex('phone')),
        email: resultSet.getString(resultSet.getColumnIndex('email')),
        tags: JSON.parse(resultSet.getString(resultSet.getColumnIndex('tags')) || '[]'),
        dnaSignature: resultSet.getString(resultSet.getColumnIndex('dna_signature'))
      });
    }
    resultSet.close();
    
    return members;
  }
}

模块清单

文件 路径 说明
数据模型 entry/src/main/ets/models/BirthdayModel.ets TeamMember + TeamState
主页面 entry/src/main/ets/pages/BirthdayReminderPage.ets 完整UI
通知管理 entry/src/main/ets/managers/NotificationManager.ets 定时提醒
数据库 entry/src/main/ets/database/BirthdayDatabase.ets 关系型存储
编辑弹窗 entry/src/main/ets/components/MemberEditDialog.ets 增改成员
详情弹窗 entry/src/main/ets/components/BirthdayDetailDialog.ets 查看详情

鸿蒙特性使用

特性 用途 API
ArkTS声明式UI 界面构建 @Component @Entry
状态管理 数据响应 @State @Observed @StorageLink
关系型数据库 本地加密存储 relationalStore
通知服务 生日提醒 notificationManager
后台任务 定时触发 WorkScheduler
文件选择 导入导出 documentPicker
分享能力 分享今日 share
日历同步 写入系统日历 calendarManager

龍魂标识

位置 内容
应用名称 龍魂生日提醒
标题栏 🐉 龍魂生日提醒
底部标识 来自龍魂生日提醒 UID9622
数据签名 SM3-哈希
DNA ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️

龍魂鸿蒙ArkTS实战完整代码

🐉 龍魂 · 鸿蒙ArkTS实战交付完成
DNA: ZHUGEXIN⚡️2025-🇨🇳🐉⚖️♠️🧚🏼‍♀️❤️♾️
UID: 9622
确认码: #CONFIRM🌌9622-ONLY-ONCE🧬LK9X-772Z
时间: 2026-07-14 05:54
模块: 6个核心文件
特性: 状态建模 · 农历公历 · 通知提醒 · 数据加密 · 办公交互

Logo

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

更多推荐