本项目旨在打造一个闭环的、高度自动化的个人数据处理系统。它始于你的日常记录,终于一份由 AI 生成的、发布在个人网站上的深度分析报告。整个系统由三大核心模块构成:Go Web 日记应用 (数据输入层)、n8n 自动化工作流 (数据处理与分析核心)、Hugo 静态网站 (数据展示层)。

系统架构总览

  1. 输入: 你通过浏览器访问 Go 日记应用,撰写每日任务、总结和心情。
  2. 存储: 数据被实时保存到 PostgreSQL 数据库。
  3. 触发: n8n 中的定时任务在每天深夜被激活。
  4. 提取: n8n 连接到数据库,抓取当天的日记数据。
  5. 分析: n8n 将数据发送给 Google Gemini,并根据预设指令生成一份包含 CSS 样式的精美 HTML 报告。
  6. 发布: n8n 将生成的 HTML 文件自动推送到托管在 GitHub 上的 Hugo 网站仓库。
  7. 部署: Netlify 或 Vercel 监测到 GitHub 仓库更新,自动重新构建并发布 Hugo 网站。
  8. 展示: 你可以在任何地方通过浏览器访问你的个人报告网站,回顾每一天的 AI 分析。

第一部分:Go Web 日记应用 (数据输入层)

核心目标: 提供一个稳定、高效、功能丰富的 Web 界面,用于日常记录。

所用工具 (Tools Used)

  • 编程语言: Go
  • Web 框架: Gin (github.com/gin-gonic/gin)
  • 数据库驱动: pq for PostgreSQL (github.com/lib/pq)
  • 数据库服务: PostgreSQL (推荐使用 SupabaseAWS RDS 等云服务)
  • 前端模板引擎: Go html/template
  • 前端核心库:
    • jQuery: 简化 DOM 操作和 AJAX。
    • CKEditor 4: 提供富文本编辑体验。
    • FullCalendar: 用于生成交互式日历。
    • Prism.js: 提供代码高亮。

实现功能 (Features Implemented)

  • 核心日记管理 (CRUD):
    • 创建 (Create): writeHandler 确保每天只创建一篇日记,避免重复。
    • 读取 (Read): detailHandler 负责读取并展示单篇日记的所有数据。
    • 更新 (Update): apiUpdateHandler 提供核心 API,用于实时更新日记。
    • 删除 (Delete): deleteHandler 允许用户删除指定的日记。
  • 极致用户体验:
    • 无感自动保存: 通过 JavaScript 监听内容变化,延迟几百毫秒后自动向后端发送更新,无需手动保存。
    • 富文本编辑: 集成 CKEditor,支持复杂格式、图片上传(需配置对象存储)和代码块。
    • 动态任务列表: 可动态增删任务,备注后任务项自动划线,模拟“已完成”。
    • 历史日记只读模式: 非当日记记自动锁定,所有输入框变为纯文本,防止误改。
  • 智能化集成:
    • 天气自动获取与更新: 加载日记时自动获取天气,并允许用户手动输入城市更新。
    • 每日心情记录: 点击 Emoji 即可通过 API 快速保存当天心情。
  • 便捷的回顾与导航:
    • 日历视图: 侧边栏日历清晰标记有日记的日期、心情和任务量,点击即可跳转。
    • “往年同期”功能: 页面底部自动异步加载并展示过去几年同一天的日记链接。

关键代码解析

1. 数据库连接与初始化 (main.go)
// main.go
func main() {
    // ...
    // DSN (数据源名称), 替换成你自己的
    dsn := "postgres://postgres.vbvkjiywdbocjpcjmtjs:519yang982@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres?sslmode=require&statement_cache_mode=none"
    db, err := sql.Open("postgres", dsn)
    if err != nil {
        log.Fatalf("打开数据库失败: %v", err)
    }
    defer db.Close()

    // --- 数据库连接池配置 (非常重要!) ---
    db.SetMaxOpenConns(25)
    db.SetMaxIdleConns(10)
    db.SetConnMaxLifetime(5 * time.Minute)

    if err = db.Ping(); err != nil {
        log.Fatal("数据库 Ping 失败:", err)
    }
    // ... 自动建表和启动服务 ...
}
2. 自动保存API (main.go)
// main.go
func apiUpdateHandler(w http.ResponseWriter, r *http.Request) {
    var payload struct {
        ID         int64       `json:"id"`
        TopText    string      `json:"top_text"`
        BottomText string      `json:"bottom_text"`
        Rows       [][2]string `json:"rows"`
    }
    if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
        http.Error(w, "JSON错误", 400); return
    }
    contentBytes, err := json.Marshal(payload.Rows)
    if err != nil {
        http.Error(w, "内容编码失败", 500); return
    }
    _, err = db.Exec(`UPDATE yys_diary SET content=$1, top_text=$2, bottom_text=$3 WHERE id=$4`,
        string(contentBytes), payload.TopText, payload.BottomText, payload.ID)
    if err != nil {
        http.Error(w, "数据库更新失败", 500); return
    }
    w.WriteHeader(200)
    json.NewEncoder(w).Encode(map[string]string{"message": "更新成功"})
}
3. 前端自动保存逻辑 (templates/detail.html)
<!-- templates/detail.html -->
<script>
  const diaryId = parseInt('{{.DiaryData.ID}}');
  let saveTimer;

  function scheduleSave() {
    clearTimeout(saveTimer);
    saveTimer = setTimeout(autoSave, 800);
  }

  function autoSave() {
    const rows = Array.from(document.querySelectorAll('#tasks .task-row')).map(row => [
        row.querySelector('.task-input')?.value || "",
        row.querySelector('.note-input')?.value || ""
    ]);
    const editorData = CKEDITOR.instances.bottomText.getData();

    fetch('/api/update', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        id: diaryId,
        top_text: document.getElementById('topTextDisplay')?.textContent.trim(),
        bottom_text: editorData,
        rows: rows
      })
    }).then(r => r.json()).then(data => console.log('[AUTO_SAVE]', data.message));
  }

  window.addEventListener('DOMContentLoaded', () => {
    document.getElementById('tasks')?.addEventListener('input', scheduleSave);
    if (CKEDITOR.instances.bottomText) {
        CKEDITOR.instances.bottomText.on('change', scheduleSave);
    }
  });
</script>

第二部分:n8n 自动化工作流 (数据处理与分析核心)

核心目标: 作为系统的大脑,自动连接所有服务,完成数据提取、AI 分析和内容发布的完整流程。

docker compose安装

services:
  n8n:
    image: n8nio/n8n:latest
    container_name: n8n
    restart: unless-stopped
    ports:
      - "8880:5678"
    environment:
      - N8N_HOST=0.0.0.0
      - N8N_PORT=5678
      - N8N_PROTOCOL=http
      - N8N_SECURE_COOKIE=false
      - GENERIC_TIMEZONE=Asia/Shanghai
      - TZ=Asia/Shanghai
      - DB_TYPE=sqlite
      - DB_SQLITE_DATABASE=/home/node/.n8n/database.sqlite
      - NODE_OPTIONS=--max-old-space-size=512
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=168
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=admin
      - N8N_BASIC_AUTH_PASSWORD=ChangeThisPassword123!
      - WEBHOOK_URL=http://你的ip:8880/
    volumes:
      - ./.n8n:/home/node/.n8n
    mem_limit: 768m
    mem_reservation: 512m
    cpus: 0.5
sudo chown -R 1000:1000 .n8n



所用工具 (Tools Used)

  • 自动化平台: n8n (云版或 Docker 自托管)
  • 核心节点 (Nodes):
    • Schedule Trigger: 定时触发器。
    • Code Node: 执行 JavaScript 代码,处理数据。
    • Postgres Node: 连接和查询 PostgreSQL。
    • Google Gemini Node (或 Basic LLM Chain Node): 与大语言模型交互。
    • GitHub Node: 执行 Git 操作,发布文件。

实现功能 (Features Implemented)

  • 定时自动化执行: 使用 Schedule Trigger 节点的 CRON 模式,实现每日定时启动,无需人工干预。
  • 动态数据提取: Code 节点生成精确的当日时间范围,Postgres 节点利用此范围,准确无误地抓取当天的日记。
  • AI 驱动的报告生成: Gemini 节点接收日记数据,并结合一个精心设计的 Prompt,指导 AI 扮演分析师角色,从多维度进行解读,并最终输出一份包含 CSS 样式的、可以直接发布的 HTML 文件。
  • 无人值守的内容发布: GitHub 节点链实现了 GitOps 流程,自动将 AI 生成的报告文件推送到 Hugo 网站仓库,触发后续的自动部署。

关键代码与配置

Node 1: Schedule Trigger
  • Mode: Cron
  • Cron Expression: 0 23 * * * (每天 23:00 执行)
Node 2: Code (准备查询参数)
  • Language: JavaScript
  • Code:
const today = new Date();
const startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate());
const endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1);
const formatForSQL = (date) => date.toISOString().slice(0, 19).replace('T', ' ');

return [{
  json: {
    startDate: formatForSQL(startDate),
    endDate: formatForSQL(endDate),
    reportDate: today.toISOString().slice(0, 10) // YYYY-MM-DD
  }
}];
Node 3: Postgres (查询当日日记)
  • Query:
SELECT title, mood, content, top_text, bottom_text
FROM yys_diary
WHERE created >= '{{ $json.startDate }}' AND created < '{{ $json.endDate }}'
ORDER BY created DESC
LIMIT 1;
Node 4: Gemini / Basic LLM Chain (AI 分析)
  • Prompt (这是灵魂,请完整复制):
你是一位结合了敏锐数据分析师、深刻心理洞察者和创意生活顾问的顶尖 AI 助手。你的任务是分析我提供的 JSON 格式的日记数据,并生成一份完整、精美、可以直接在浏览器中打开的 HTML 格式分析报告(加个好看的css背景)。

**遵循以下指令:**

请确保输出包含 `<!DOCTYPE html>`, `<html>`, `<body>` 标签,并使用 UTF-8 编码。例如:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>分析结果</title>
</head>
<body>
    <!-- 在这里生成您的分析内容,例如使用 <h1>, <p>, <strong> 等标签 -->
    <h1>日记摘要分析</h1>
    <p>这是对...的分析。</p>
</body>
</html>

现在,请根据以下数据生成报告:
标题: {{ $json.title }}
心情: {{ $json.mood }}
内容: {{ $json.content }}
天气: {{ $json.top_text }}
总结:{{ $json.bottom_text }}
最后给出建议(有创意的)。
Node 5: GitHub (发布报告)
  • File Path: content/reports/{{ $json.reportDate }}.html
  • Content: {{ $('Google Gemini').first().json.output }}
  • Commit Message: [Automated] Add daily report for {{ $json.reportDate }}

image.png


第三部分:Hugo 静态网站 (数据展示层)

核心目标: 提供一个极速、美观、免维护的网站来归档和展示所有 AI 生成的每日分析报告。

所用工具 (Tools Used)

  • 静态网站生成器: Hugo
  • 版本控制与托管: Git, GitHub
  • CI/CD & 网站托管: Netlify / Vercel / GitHub Pages

实现功能 (Features Implemented)

  • 报告自动聚合: 网站首页自动遍历 content/reports/ 目录下的所有报告文件并生成列表。
  • 按日期智能排序: 利用文件名即日期的特性,将最新的报告自动展示在最顶部。
  • 全自动化部署 (GitOps): n8n 推送更新到 GitHub 后,Netlify/Vercel 等平台会自动构建并发布新版本的网站。
  • 极致的性能与安全性: 纯静态网站,访问速度快,无数据库,安全性高。

关键代码解析 (layouts/index.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>{{ .Site.Title }} - 个人 AI 分析报告</title>
    <style>
        body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; max-width: 800px; margin: 40px auto; padding: 20px; background-color: #f5f7fa; }
        h1 { text-align: center; }
        ul { list-style-type: none; padding: 0; }
        li { background-color: #fff; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.05); margin: 12px 0; transition: all 0.2s; }
        li:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.1); }
        a { display: block; padding: 18px 22px; text-decoration: none; color: #3498db; font-size: 1.1em; }
    </style>
</head>
<body>
    <h1>{{ .Site.Title }}</h1>
    <p style="text-align: center; color: #7f8c8d;">{{ .Site.Params.description }}</p>

    <h2>所有报告</h2>
    <ul>
        {{/*
            这是 Hugo 的核心循环逻辑:
            1. where .Site.Pages "Section" "reports": 只筛选出放在 "content/reports" 文件夹下的页面。
            2. .ByTitle.Reverse: 因为文件名是日期 (如 2025-11-02.html),按标题(文件名)倒序,就能实现按日期降序。
        */}}
        {{ range (where .Site.Pages "Section" "reports").ByTitle.Reverse }}
            <li>
                <a href="{{ .RelPermalink }}">
                    报告日期:{{ .File.BaseFileName }}
                </a>
            </li>
        {{ end }}
    </ul>
</body>
</html>