系统架构与核心流程

5/8/2026

Holdings Monitor Web - 系统架构与核心流程


一、项目概述

项目名称:Holdings Monitor Web
技术栈:Next.js 16 + TypeScript + Supabase + Vercel + OpenClaw + Tailwind CSS
核心功能:习惯打卡、飞书消息通知、持仓监控、悄悄话记录


二、系统架构

2.1 整体架构图

┌─────────────────────────────────────────────────────────────────┐
│                     前端层 (Frontend)                           │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────────┐   │
│  │  Habits  │  │ Settings │  │  Login   │  │   Whispers   │   │
│  │  (习惯)  │  │ (设置)   │  │ (登录)   │  │   (悄悄话)   │   │
│  └────┬─────┘  └────┬─────┘  └────┬─────┘  └──────┬───────┘   │
└───────┼─────────────┼─────────────┼────────────────┼───────────┘
        │             │             │                │
        ▼             ▼             ▼                ▼
┌─────────────────────────────────────────────────────────────────┐
│                     API 层 (Backend)                            │
│  ┌─────────────────────────────────────────────────────────┐   │
│  │  /api/reminder     → 习惯提醒核心API                    │   │
│  │  /api/feishu/webhook → 飞书消息回调                     │   │
│  │  /api/test-notification → 通知测试                     │   │
│  └─────────────────────────────────────────────────────────┘   │
└───────┬────────────────────────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────────────────────────────────┐
│                     数据层 (Database)                           │
│  ┌────────────┐  ┌──────────────┐  ┌──────────────────┐       │
│  │  habits    │  │habit_records │  │system_settings   │       │
│  │  (习惯表)  │  │  (打卡记录表) │  │  (系统设置表)    │       │
│  └────────────┘  └──────────────┘  └──────────────────┘       │
└─────────────────────────────────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────────────────────────────────┐
│                     外部服务 (External)                         │
│  ┌──────────┐  ┌──────────┐  ┌──────────────┐                 │
│  │ Supabase │  │OpenClaw  │  │   Feishu     │                 │
│  │ (数据库) │  │(消息通道)│  │  (飞书通知)   │                 │
│  └──────────┘  └──────────┘  └──────────────┘                 │
└─────────────────────────────────────────────────────────────────┘

2.2 模块划分

| 模块 | 功能职责 | 关键文件 | |------|----------|----------| | 习惯管理 | 习惯CRUD、打卡记录 | app/habits/, lib/habits.ts | | 提醒系统 | 定时提醒、消息发送 | app/api/reminder/route.ts | | 飞书集成 | 消息监听、命令处理 | app/api/feishu/webhook/route.ts, scripts/listen-feishu.sh | | 认证系统 | 用户登录、RLS权限 | contexts/AuthContext.tsx, scripts/setup-auth-rls.ts | | 数据访问 | 数据库操作封装 | lib/supabase.ts, lib/supabase-client.ts |

2.3 核心目录结构

holdings-monitor-web/
├── app/                    # Next.js App Router
│   ├── api/                # API 路由
│   │   ├── reminder/       # 提醒核心API
│   │   └── feishu/webhook/ # 飞书回调
│   ├── habits/             # 习惯打卡页面
│   ├── settings/           # 设置页面
│   └── whispers/           # 悄悄话页面
├── lib/                    # 工具库
│   ├── habits.ts           # 习惯数据访问
│   ├── supabase.ts         # 服务端客户端(Admin)
│   └── supabase-client.ts  # 客户端(SSR)
├── scripts/                # 运维脚本
│   ├── send-reminders.sh   # 发送提醒脚本
│   ├── listen-feishu.sh    # 飞书消息监听
│   └── setup-cron.sh       # OpenClaw Cron配置
└── supabase/               # 数据库迁移脚本

三、核心功能工作流程

3.1 习惯打卡流程

sequenceDiagram
    participant User as 用户
    participant Frontend as 前端
    participant API as API Route
    participant DB as Supabase

    User->>Frontend: 打开习惯页面
    Frontend->>API: GET /api/habits (获取习惯列表)
    API->>DB: SELECT * FROM habits WHERE user_id = $1
    DB-->>API: 返回习惯列表
    API-->>Frontend: 返回习惯数据
    Frontend-->>User: 展示习惯卡片

    User->>Frontend: 点击打卡按钮
    Frontend->>API: POST /api/reminder (action: process_reply)
    API->>DB: INSERT/UPDATE habit_records
    DB-->>API: 操作成功
    API-->>Frontend: 返回成功状态
    Frontend-->>User: 显示打卡成功

3.2 定时提醒流程

sequenceDiagram
    participant Cron as OpenClaw Cron
    participant Script as send-reminders.sh
    participant API as /api/reminder
    participant DB as Supabase
    participant OpenClaw as OpenClaw
    participant Feishu as 飞书

    Cron->>Script: 定时触发(每小时)
    Script->>API: POST action=send_all_reminders
    API->>DB: SELECT * FROM system_settings WHERE openclaw_enabled=true
    DB-->>API: 返回用户设置列表
    
    loop 遍历每个用户
        API->>DB: SELECT * FROM habits WHERE user_id = $1
        DB-->>API: 返回用户习惯
        API->>DB: SELECT status FROM habit_records WHERE date = today
        DB-->>API: 返回打卡状态
        API->>API: 过滤未完成且到提醒时间的习惯
        API->>OpenClaw: openclaw message send
        OpenClaw->>Feishu: 发送提醒消息
    end
    
    API-->>Script: 返回发送结果
    Script-->>Cron: 执行完成

3.3 飞书消息处理流程

sequenceDiagram
    participant User as 用户(飞书)
    participant OpenClaw as OpenClaw
    participant Script as listen-feishu.sh
    participant API as /api/reminder
    participant DB as Supabase

    User->>OpenClaw: 发送消息 "/打卡 1"
    Script->>OpenClaw: openclaw message read (轮询)
    OpenClaw-->>Script: 返回新消息
    Script->>Script: 检查是否已处理(去重)
    Script->>API: POST action=process_reply, message="/打卡 1"
    API->>DB: 获取用户习惯列表(按规则过滤)
    DB-->>API: 返回习惯数据
    API->>API: 解析命令,定位目标习惯
    API->>DB: UPDATE habit_records SET status='completed'
    DB-->>API: 更新成功
    API-->>Script: 返回处理结果
    Script->>OpenClaw: openclaw message send (回复)
    OpenClaw->>User: 发送确认消息

四、小记功能(博客/笔记展示)

4.1 功能概述

小记功能用于展示用户的博客文章和笔记,支持从 GitHub 仓库同步内容,展示目录结构和最后更新时间。

4.2 GitHub 目录结构展示

MyObsidian/                    # GitHub 仓库根目录
├── Projects/                  # 项目文件夹
│   ├── My Blog/               # 个人博客
│   │   └── 我的站点技术栈.md   # 博客文章
│   └── Holdings Monitor/      # 当前项目
│       └── 系统架构与核心流程.md
├── Tools/                     # 工具笔记
│   ├── Obsidian CLI 功能分析.md
│   └── Obsidian 功能与配置指南.md
├── Goals/                     # 目标管理
│   └── 目标管理指南.md
├── Whispers/                  # 悄悄话(私密笔记)
│   ├── 2026-04-23-0648.md
│   └── 2026-04-23-0649.md
└── 欢迎.md                    # 首页欢迎文档

4.3 文件列表展示设计

根据用户界面设计,小记页面展示如下信息:

| 显示项 | 说明 | 数据源 | |--------|------|--------| | 文件名 | 笔记标题(不含 .md 后缀) | GitHub 文件列表 | | 最后更新时间 | 文件最后 commit 日期 | GitHub API - last_modified | | 目录层级 | 文件所在路径 | GitHub API - path |

4.4 时间逻辑说明

最后编辑时间的获取优先级:

  1. GitHub Commit 时间(最高优先级)

    • 通过 GitHub API 获取文件的最后 commit 信息
    • 格式:YYYY-MM-DD(如 2026-04-29
  2. 文件元数据时间(备选)

    • 读取文件的 updated_at 字段
    • 适用于本地数据库存储的笔记

4.5 小记页面工作流程

sequenceDiagram
    participant User as 用户
    participant Frontend as 小记页面
    participant API as GitHub API / 本地 API
    participant GitHub as GitHub Repository

    User->>Frontend: 访问小记页面
    Frontend->>API: 请求文件列表
    alt 从 GitHub 获取(博客文章)
        API->>GitHub: GET /repos/{owner}/{repo}/contents/{path}
        GitHub-->>API: 返回目录结构和文件信息
        API-->>Frontend: 返回文件列表(含 last_modified)
    else 从本地数据库获取(悄悄话)
        API->>API: SELECT * FROM whispers ORDER BY updated_at DESC
        API-->>Frontend: 返回笔记列表(含 updated_at)
    end
    Frontend-->>User: 展示文件列表(文件名 + 最后更新时间)
    
    User->>Frontend: 点击文件
    Frontend->>API: 请求文件内容
    API-->>Frontend: 返回 Markdown 内容
    Frontend-->>User: 渲染并展示笔记详情

五、关键技术要点

5.1 数据库设计

habits 表(习惯表): | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | user_id | UUID | 用户ID(null表示共享习惯) | | title | VARCHAR | 习惯名称 | | description | TEXT | 描述 | | use_custom_reminder | BOOLEAN | 是否使用自定义提醒时间 | | reminder_time | TIME | 提醒时间 | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 最后编辑时间(用于排序展示) |

habit_records 表(打卡记录表): | 字段 | 类型 | 说明 | |------|------|------| | id | UUID | 主键 | | habit_id | UUID | 关联习惯ID | | date | DATE | 打卡日期 | | status | VARCHAR | completed/skipped/failed | | created_at | TIMESTAMP | 创建时间 | | updated_at | TIMESTAMP | 最后编辑时间(打卡状态变更时间) |

5.2 时间处理逻辑

// UTC 转北京时间
const now = new Date();
const utcHour = now.getUTCHours();
const beijingHour = (utcHour + 8) % 24;
const beijingDate = new Date(now.getTime() + 8 * 60 * 60 * 1000);
const dateStr = beijingDate.toISOString().split('T')[0];

5.3 飞书命令格式

| 命令 | 功能 | |------|------| | /打卡 1 | 完成第1个待打卡习惯 | | /打卡 完成 | 完成所有待打卡习惯 | | /打卡 暂停 1 | 暂停第1个习惯 | | /打卡 未完成 1 | 标记第1个习惯为未完成 |


六、部署与运维

6.1 部署架构

┌─────────────────┐
│   Vercel        │  ← 前端部署(自动CI/CD)
│  Frontend       │
└────────┬────────┘
         │ API Requests
         ▼
┌─────────────────┐
│  Supabase       │  ← 数据库托管
│  PostgreSQL     │
└────────┬────────┘
         │ Webhook
         ▼
┌─────────────────┐
│  OpenClaw       │  ← 消息通道+定时任务
│  Cron + Message │
└────────┬────────┘
         │ Send Message
         ▼
┌─────────────────┐
│   Feishu        │  ← 飞书机器人
│  Webhook URL    │
└─────────────────┘

6.2 定时任务配置

# OpenClaw Cron 配置
openclaw cron add \
  --cron "0 * * * *" \
  --agent main \
  --message "执行命令: /Users/su/code/holdings-monitor-web/scripts/send-reminders.sh" \
  --name "habit-reminder" \
  --description "每小时检查并发送习惯打卡提醒" \
  --no-deliver

6.3 环境变量

| 变量名 | 用途 | |--------|------| | NEXT_PUBLIC_SUPABASE_URL | Supabase 数据库地址 | | NEXT_PUBLIC_SUPABASE_ANON_KEY | 客户端访问密钥 | | SUPABASE_SERVICE_ROLE_KEY | 服务端密钥(绕过RLS) | | SUPABASE_ACCESS_TOKEN | Supabase CLI 认证令牌(执行数据库迁移用) | | NEXT_PUBLIC_APP_URL | 应用域名 | | OPENCLAW_CHANNEL_ID | OpenClaw 通道ID | | OPENCLAW_TARGET_ID | OpenClaw 目标ID |


七、核心 API 接口

7.1 /api/reminder

| Action | 功能 | 参数 | |--------|------|------| | send_reminder | 发送单个用户提醒 | userId | | send_all_reminders | 批量发送所有用户提醒 | - | | process_reply | 处理用户打卡回复 | userId, message | | get_pending_reminders | 获取待发送提醒列表 | - | | debug_settings | 调试系统设置 | - | | debug_habits | 调试习惯数据 | - | | debug_records | 调试打卡记录 | - |


八、安全与权限

8.1 RLS (行级安全)

  • habits 表:用户只能访问自己创建的习惯或共享习惯(user_id IS NULL)
  • habit_records 表:用户只能访问自己的打卡记录
  • system_settings 表:用户只能访问自己的设置

8.2 服务端操作

所有 API Route 使用 supabaseAdmin(服务端密钥)绕过 RLS,确保:

  • 定时任务能访问所有用户数据
  • 飞书回调能正确处理用户命令
  • 避免因权限问题导致的数据不一致

九、故障排查指南

9.1 提醒不发送

  1. 检查 OpenClaw Cron 状态:openclaw cron list
  2. 检查数据库设置:curl -X POST /api/reminder -d '{"action": "debug_settings"}'
  3. 检查服务器时间:curl -X POST /api/reminder -d '{"action": "debug_time"}'

9.2 打卡不生效

  1. 检查习惯顺序:curl -X POST /api/reminder -d '{"action": "get_pending_reminders"}'
  2. 检查打卡记录:curl -X POST /api/reminder -d '{"action": "debug_records"}'
  3. 检查习惯数据:curl -X POST /api/reminder -d '{"action": "debug_habits"}'

9.3 飞书消息不响应

  1. 检查监听脚本:ps aux | grep listen-feishu
  2. 检查 OpenClaw 连接:openclaw status
  3. 检查飞书 Webhook 配置

十、总结

核心价值:通过飞书消息驱动的习惯打卡系统,实现:

  1. 自动化提醒:定时检查并推送打卡提醒
  2. 便捷操作:通过飞书命令快速完成打卡
  3. 数据同步:确保多端数据一致性
  4. 灵活扩展:支持自定义提醒时间、多通知渠道

技术亮点

  • 使用 OpenClaw 作为消息中间件,解耦消息发送与业务逻辑
  • 服务端统一使用 Admin 密钥,避免 RLS 权限问题
  • 北京时间处理,确保跨时区正确运行