{
  "name": "Firestore \uc815\uc624 \uc694\uc57d \ub9ac\ud3ec\ud2b8 (Gemini)",
  "nodes": [
    {
      "parameters": {
        "triggerTimes": {
          "item": [
            {
              "mode": "everyDay",
              "hour": 12,
              "minute": 0
            }
          ]
        },
        "timezone": "Asia/Seoul"
      },
      "id": "cron-trigger",
      "name": "Cron (\ub9e4\uc77c 12:00 KST)",
      "type": "n8n-nodes-base.cron",
      "typeVersion": 1,
      "position": [
        260,
        260
      ]
    },
    {
      "parameters": {
        "functionCode": "// \uc624\ub298 00:00 KST ~ \ud604\uc7ac(\uc815\uc624) \uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0\nconst now = new Date();\nconst y = now.getFullYear();\nconst m = now.getMonth();\nconst d = now.getDate();\nconst start = new Date(y, m, d, 0, 0, 0);\nconst end = now;\n\n// \uc5b4\uc81c \uac19\uc740 \uc2dc\uac04\ub300\ub3c4 \uacc4\uc0b0 (\ube44\uad50\uc6a9)\nconst yesterday = new Date(start);\nyesterday.setDate(yesterday.getDate() - 1);\nconst yesterdayEnd = new Date(end);\nyesterdayEnd.setDate(yesterdayEnd.getDate() - 1);\n\nreturn [{\n  json: {\n    startISO: start.toISOString(),\n    endISO: end.toISOString(),\n    yesterdayStartISO: yesterday.toISOString(),\n    yesterdayEndISO: yesterdayEnd.toISOString(),\n    dateRange: `${start.toISOString().split('T')[0]} 00:00 ~ ${end.toISOString().split('T')[0]} 12:00`\n  }\n}];"
      },
      "id": "time-calculator",
      "name": "\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        520,
        260
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "projectId": "what2eat-e21e3",
        "collection": "reports",
        "query": {
          "queryFieldsUi": {
            "queryFieldsValues": [
              {
                "field": "created_at",
                "operator": ">=",
                "value": "={{$json.startISO}}"
              },
              {
                "field": "created_at",
                "operator": "<=",
                "value": "={{$json.endISO}}"
              }
            ]
          }
        },
        "limit": 1000
      },
      "id": "firestore-today",
      "name": "Firestore: \uc624\ub298 \ub370\uc774\ud130 \uc870\ud68c",
      "type": "n8n-nodes-base.googleFirebaseCloudFirestore",
      "typeVersion": 1,
      "credentials": {
        "googleFirebaseCloudFirestoreOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "position": [
        780,
        200
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "projectId": "what2eat-e21e3",
        "collection": "reports",
        "query": {
          "queryFieldsUi": {
            "queryFieldsValues": [
              {
                "field": "created_at",
                "operator": ">=",
                "value": "={{$json.yesterdayStartISO}}"
              },
              {
                "field": "created_at",
                "operator": "<=",
                "value": "={{$json.yesterdayEndISO}}"
              }
            ]
          }
        },
        "limit": 1000
      },
      "id": "firestore-yesterday",
      "name": "Firestore: \uc5b4\uc81c \ub370\uc774\ud130 \uc870\ud68c",
      "type": "n8n-nodes-base.googleFirebaseCloudFirestore",
      "typeVersion": 1,
      "credentials": {
        "googleFirebaseCloudFirestoreOAuth2Api": {
          "name": "<your credential>"
        }
      },
      "position": [
        780,
        320
      ]
    },
    {
      "parameters": {
        "functionCode": "// \uc624\ub298\uacfc \uc5b4\uc81c \ub370\uc774\ud130\ub97c \uc9d1\uacc4\ud558\uace0 \ube44\uad50 \ubd84\uc11d\uc6a9 \ud14d\uc2a4\ud2b8 \uc0dd\uc131\nconst todayData = $input.first().json;\nconst yesterdayData = $input.last().json;\n\n// \uc624\ub298 \ub370\uc774\ud130 \uc9d1\uacc4\nconst todayItems = todayData || [];\nconst todayByCategory = {};\nlet todayTotalValue = 0;\n\nfor (const item of todayItems) {\n  const category = item.category || '\uae30\ud0c0';\n  const value = Number(item.value || 0);\n  \n  if (!todayByCategory[category]) {\n    todayByCategory[category] = { count: 0, sum: 0, items: [] };\n  }\n  \n  todayByCategory[category].count += 1;\n  todayByCategory[category].sum += value;\n  todayByCategory[category].items.push({\n    title: item.title || '\uc81c\ubaa9 \uc5c6\uc74c',\n    value: value,\n    created_at: item.created_at\n  });\n  todayTotalValue += value;\n}\n\n// \uc5b4\uc81c \ub370\uc774\ud130 \uc9d1\uacc4\nconst yesterdayItems = yesterdayData || [];\nconst yesterdayByCategory = {};\nlet yesterdayTotalValue = 0;\n\nfor (const item of yesterdayItems) {\n  const category = item.category || '\uae30\ud0c0';\n  const value = Number(item.value || 0);\n  \n  if (!yesterdayByCategory[category]) {\n    yesterdayByCategory[category] = { count: 0, sum: 0 };\n  }\n  \n  yesterdayByCategory[category].count += 1;\n  yesterdayByCategory[category].sum += value;\n  yesterdayTotalValue += value;\n}\n\n// \uac01 \uce74\ud14c\uace0\ub9ac\ubcc4 \uc0c1\uc704 \ud56d\ubaa9 \uc815\ub82c\nfor (const category of Object.keys(todayByCategory)) {\n  todayByCategory[category].items.sort((a, b) => b.value - a.value);\n  todayByCategory[category].top5 = todayByCategory[category].items.slice(0, 5);\n}\n\n// \uc804\uccb4 \uc0c1\uc704 \ud56d\ubaa9\nconst allTodayItems = [];\nfor (const categoryInfo of Object.values(todayByCategory)) {\n  allTodayItems.push(...categoryInfo.items);\n}\nallTodayItems.sort((a, b) => b.value - a.value);\n\n// \ubcc0\ud654\uc728 \uacc4\uc0b0\nconst valueChangePercent = yesterdayTotalValue > 0 ? \n  ((todayTotalValue - yesterdayTotalValue) / yesterdayTotalValue * 100).toFixed(1) : 0;\n\n// Gemini\uc5d0 \uc804\ub2ec\ud560 \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131\nconst analysisText = `\ud83d\udcca **Firestore \ub370\uc774\ud130 \ubd84\uc11d \uc694\uccad**\n\n**\uae30\uac04**: ${$json.dateRange}\n**\ucd1d \ubb38\uc11c \uc218**: ${todayItems.length}\uac1c (\uc5b4\uc81c: ${yesterdayItems.length}\uac1c)\n**\ucd1d \uac12**: ${todayTotalValue.toFixed(2)} (\uc5b4\uc81c: ${yesterdayTotalValue.toFixed(2)}, \ubcc0\ud654: ${valueChangePercent}%)\n\n**\uce74\ud14c\uace0\ub9ac\ubcc4 \uc9d1\uacc4 (\uc624\ub298)**:\n` +\nObject.entries(todayByCategory).map(([category, info]) => {\n  const yesterdayInfo = yesterdayByCategory[category] || { count: 0, sum: 0 };\n  const countChange = yesterdayInfo.count > 0 ? \n    ((info.count - yesterdayInfo.count) / yesterdayInfo.count * 100).toFixed(1) : 'N/A';\n  const valueChange = yesterdayInfo.sum > 0 ? \n    ((info.sum - yesterdayInfo.sum) / yesterdayInfo.sum * 100).toFixed(1) : 'N/A';\n  \n  return `- **${category}**: \uac1c\uc218=${info.count} (${countChange}%), \ud569\uacc4=${info.sum.toFixed(2)} (${valueChange}%)\\n  \uc0c1\uc7045: ${info.top5.map(item => `${item.title}(${item.value})`).join(', ')}`;\n}).join('\\n') +\n`\\n\\n**\uc804\uccb4 \uc0c1\uc704 10\uac1c \ud56d\ubaa9**:\\n` +\nallTodayItems.slice(0, 10).map((item, i) => `${i+1}. ${item.title}: ${item.value}`).join('\\n') +\n`\\n\\n**\ud2b9\uc774\uc0ac\ud56d**:\\n- \ucd1d \uac12 \ubcc0\ud654: ${valueChangePercent}%\\n- \uac00\uc7a5 \ud65c\ubc1c\ud55c \uce74\ud14c\uace0\ub9ac: ${Object.entries(todayByCategory).sort((a,b) => b[1].count - a[1].count)[0]?.[0] || 'N/A'}\\n- \ucd5c\uace0\uac12 \ud56d\ubaa9: ${allTodayItems[0]?.title || 'N/A'} (${allTodayItems[0]?.value || 0})`;\n\nreturn [{\n  json: {\n    analysisText,\n    todayData: {\n      totalCount: todayItems.length,\n      totalValue: todayTotalValue,\n      categories: todayByCategory,\n      topItems: allTodayItems.slice(0, 10)\n    },\n    yesterdayData: {\n      totalCount: yesterdayItems.length,\n      totalValue: yesterdayTotalValue\n    },\n    comparison: {\n      valueChangePercent: parseFloat(valueChangePercent),\n      countChangePercent: yesterdayItems.length > 0 ? \n        ((todayItems.length - yesterdayItems.length) / yesterdayItems.length * 100).toFixed(1) : 'N/A'\n    }\n  }\n}];"
      },
      "id": "data-aggregator",
      "name": "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        1040,
        260
      ]
    },
    {
      "parameters": {
        "model": "gemini-pro",
        "options": {
          "temperature": 0.2,
          "maxTokens": 2000
        },
        "systemMessage": "\ub2f9\uc2e0\uc740 \ub370\uc774\ud130 \ubd84\uc11d \uc804\ubb38\uac00\uc785\ub2c8\ub2e4. Firestore \ub370\uc774\ud130\ub97c \ubd84\uc11d\ud558\uc5ec \uba85\ud655\ud558\uace0 \uc2e4\ud589 \uac00\ub2a5\ud55c \uc778\uc0ac\uc774\ud2b8\uac00 \ud3ec\ud568\ub41c \ud55c\uad6d\uc5b4 \ub9ac\ud3ec\ud2b8\ub97c \uc791\uc131\ud574\uc8fc\uc138\uc694. \ub9c8\ud06c\ub2e4\uc6b4 \ud615\uc2dd\uc744 \uc0ac\uc6a9\ud558\uace0, \uad6c\uccb4\uc801\uc778 \uc218\uce58\uc640 \ubcc0\ud654\uc728\uc744 \ud3ec\ud568\ud558\uc5ec \uc804\ubb38\uc801\uc778 \ubd84\uc11d\uc744 \uc81c\uacf5\ud574\uc8fc\uc138\uc694.",
        "text": "={{$json.analysisText}}\n\n\uc704 \ub370\uc774\ud130\ub97c \ubc14\ud0d5\uc73c\ub85c \ub2e4\uc74c\uc744 \ud3ec\ud568\ud55c \uc815\uc624 \uc694\uc57d \ub9ac\ud3ec\ud2b8\ub97c \uc791\uc131\ud574\uc8fc\uc138\uc694:\n1. \uc8fc\uc694 \ud2b8\ub80c\ub4dc \ubc0f \ubcc0\ud654\n2. \uce74\ud14c\uace0\ub9ac\ubcc4 \uc0c1\uc138 \ubd84\uc11d\n3. \ud2b9\uc774\uc0ac\ud56d \ubc0f \uc774\uc0c1 \uc9d5\ud6c4\n4. \uc2e4\ud589 \uac00\ub2a5\ud55c \uc778\uc0ac\uc774\ud2b8 3\uac00\uc9c0\n5. \ub0b4\uc77c\uc744 \uc704\ud55c \uad8c\uc7a5\uc0ac\ud56d\n\n\ub9c8\ud06c\ub2e4\uc6b4 \ud615\uc2dd\uc73c\ub85c \uc791\uc131\ud574\uc8fc\uc138\uc694."
      },
      "id": "gemini-analyzer",
      "name": "Gemini AI \ubd84\uc11d",
      "type": "n8n-nodes-base.googleGemini",
      "typeVersion": 1,
      "credentials": {
        "googleGeminiApi": {
          "name": "<your credential>"
        }
      },
      "position": [
        1300,
        260
      ]
    },
    {
      "parameters": {
        "functionCode": "// \ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305\nconst geminiAnalysis = $json.text || $json.data || '\ubd84\uc11d \uacb0\uacfc\ub97c \uac00\uc838\uc62c \uc218 \uc5c6\uc2b5\ub2c8\ub2e4.';\nconst aggregatedData = $('\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131').first().json;\nconst timeInfo = $('\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0').first().json;\n\nconst finalReport = `\ud83d\udcca **Firestore \uc815\uc624 \uc694\uc57d \ub9ac\ud3ec\ud2b8**\n\ud83d\udcc5 **\ubd84\uc11d \uae30\uac04**: ${timeInfo.dateRange}\n\ud83d\udcc8 **\ub370\uc774\ud130 \ud604\ud669**: \ucd1d ${aggregatedData.todayData.totalCount}\uac1c \ubb38\uc11c, \ucd1d \uac12 ${aggregatedData.todayData.totalValue.toFixed(2)}\n\ud83d\udcca **\uc804\uc77c \ub300\ube44**: \uac12 ${aggregatedData.comparison.valueChangePercent}% \ubcc0\ud654, \ubb38\uc11c \uc218 ${aggregatedData.comparison.countChangePercent}% \ubcc0\ud654\n\n---\n\n${geminiAnalysis}\n\n---\n\n\ud83d\udccb **\uc694\uc57d \ud1b5\uacc4**:\n- \ubd84\uc11d \uc2dc\uac04: ${new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' })}\n- \ub370\uc774\ud130 \uc18c\uc2a4: Firestore \uceec\ub809\uc158 'reports'\n- \ubd84\uc11d \ub3c4\uad6c: Google Gemini AI`;\n\nreturn [{\n  json: {\n    finalReport,\n    summary: {\n      timestamp: new Date().toISOString(),\n      dataCount: aggregatedData.todayData.totalCount,\n      totalValue: aggregatedData.todayData.totalValue,\n      changePercent: aggregatedData.comparison.valueChangePercent\n    }\n  }\n}];"
      },
      "id": "report-formatter",
      "name": "\ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305",
      "type": "n8n-nodes-base.function",
      "typeVersion": 2,
      "position": [
        1560,
        260
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "channel": "#daily-reports",
        "text": "={{$json.finalReport}}",
        "otherOptions": {
          "thread_ts": "",
          "reply_broadcast": false
        }
      },
      "id": "slack-notification",
      "name": "Slack \uc54c\ub9bc \uc804\uc1a1",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "position": [
        1820,
        260
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "channel": "#daily-reports",
        "text": "\u274c **Firestore \uc815\uc624 \uc694\uc57d \uc2e4\ud328**\n\n\uc6cc\ud06c\ud50c\ub85c\uc6b0 \uc2e4\ud589 \uc911 \uc624\ub958\uac00 \ubc1c\uc0dd\ud588\uc2b5\ub2c8\ub2e4.\n\n**\uc624\ub958 \uc815\ubcf4**:\n- \uc2dc\uac04: {{new Date().toLocaleString('ko-KR', { timeZone: 'Asia/Seoul' })}}\n- \ub178\ub4dc: {{$node.name}}\n- \uc624\ub958: {{$json.error.message || '\uc54c \uc218 \uc5c6\ub294 \uc624\ub958'}}\n\n\uad00\ub9ac\uc790\uc5d0\uac8c \ubb38\uc758\ud574\uc8fc\uc138\uc694.",
        "otherOptions": {}
      },
      "id": "slack-error",
      "name": "Slack \uc624\ub958 \uc54c\ub9bc",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "credentials": {
        "slackApi": {
          "name": "<your credential>"
        }
      },
      "position": [
        1300,
        400
      ]
    }
  ],
  "connections": {
    "Cron (\ub9e4\uc77c 12:00 KST)": {
      "main": [
        [
          {
            "node": "\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\uc2dc\uac04 \ubc94\uc704 \uacc4\uc0b0": {
      "main": [
        [
          {
            "node": "Firestore: \uc624\ub298 \ub370\uc774\ud130 \uc870\ud68c",
            "type": "main",
            "index": 0
          },
          {
            "node": "Firestore: \uc5b4\uc81c \ub370\uc774\ud130 \uc870\ud68c",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Firestore: \uc624\ub298 \ub370\uc774\ud130 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Firestore: \uc5b4\uc81c \ub370\uc774\ud130 \uc870\ud68c": {
      "main": [
        [
          {
            "node": "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ub370\uc774\ud130 \uc9d1\uacc4 & \ubd84\uc11d \ud14d\uc2a4\ud2b8 \uc0dd\uc131": {
      "main": [
        [
          {
            "node": "Gemini AI \ubd84\uc11d",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini AI \ubd84\uc11d": {
      "main": [
        [
          {
            "node": "\ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ucd5c\uc885 \ub9ac\ud3ec\ud2b8 \ud3ec\ub9f7\ud305": {
      "main": [
        [
          {
            "node": "Slack \uc54c\ub9bc \uc804\uc1a1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "createdAt": "2024-01-01T00:00:00.000Z",
      "updatedAt": "2024-01-01T00:00:00.000Z",
      "id": "firestore-summary",
      "name": "Firestore Summary"
    }
  ],
  "triggerCount": 1,
  "updatedAt": "2024-01-01T00:00:00.000Z",
  "versionId": "1"
}