{
  "name": "WF-Discovery - XHS\u5185\u5bb9\u53d1\u73b0",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "minutes",
              "minutesInterval": 30
            }
          ]
        }
      },
      "id": "schedule-trigger",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        250,
        300
      ],
      "notes": "\u6bcf30\u5206\u949f\u81ea\u52a8\u6267\u884c"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "xhs-discovery",
        "responseMode": "responseNode"
      },
      "id": "webhook-trigger",
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        250,
        500
      ],
      "notes": "\u624b\u52a8\u89e6\u53d1\u5165\u53e3"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "app_id",
              "value": "={{ $env.LARK_APP_ID }}"
            },
            {
              "name": "app_secret",
              "value": "={{ $env.LARK_APP_SECRET }}"
            }
          ]
        },
        "options": {}
      },
      "id": "get-lark-token",
      "name": "Get Lark Token",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        500,
        400
      ],
      "notes": "\u83b7\u53d6\u98de\u4e66\u8bbf\u95ee\u4ee4\u724c"
    },
    {
      "parameters": {
        "method": "GET",
        "url": "={{ 'https://open.feishu.cn/open-apis/bitable/v1/apps/' + $env.LARK_APP_TOKEN + '/tables/' + $env.LARK_KEYWORDS_TABLE_ID + '/records' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['Get Lark Token'].json.tenant_access_token }}"
            }
          ]
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "filter",
              "value": "CurrentValue.[status]=\"\u5f85\u91c7\u96c6\""
            },
            {
              "name": "page_size",
              "value": "5"
            }
          ]
        },
        "options": {}
      },
      "id": "query-keywords",
      "name": "Query Keywords",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        750,
        400
      ],
      "notes": "\u67e5\u8be2\u5f85\u91c7\u96c6\u7684\u5173\u952e\u8bcd"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-records",
              "leftValue": "={{ $json.data.items.length }}",
              "rightValue": "0",
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "check-has-keywords",
      "name": "Has Keywords?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1000,
        400
      ],
      "notes": "\u68c0\u67e5\u662f\u5426\u6709\u5f85\u91c7\u96c6\u5173\u952e\u8bcd"
    },
    {
      "parameters": {
        "jsCode": "// \u63d0\u53d6\u5173\u952e\u8bcd\u5217\u8868\nconst items = $input.first().json.data.items || [];\n\nconst keywords = items.map(item => ({\n  record_id: item.record_id,\n  keyword: item.fields.keyword,\n  category: item.fields.category,\n  min_likes: item.fields.min_likes || 100,\n  crawl_limit: item.fields.crawl_limit || 10\n}));\n\nreturn keywords.map(k => ({ json: k }));"
      },
      "id": "extract-keywords",
      "name": "Extract Keywords",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1250,
        300
      ],
      "notes": "\u63d0\u53d6\u5173\u952e\u8bcd\u6570\u636e"
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{ 'https://open.feishu.cn/open-apis/bitable/v1/apps/' + $env.LARK_APP_TOKEN + '/tables/' + $env.LARK_KEYWORDS_TABLE_ID + '/records/' + $json.record_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['Get Lark Token'].json.tenant_access_token }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"fields\": {\n    \"status\": \"\u91c7\u96c6\u4e2d\",\n    \"locked_at\": \"{{ $now.toISO() }}\"\n  }\n}",
        "options": {}
      },
      "id": "lock-keyword",
      "name": "Lock Keyword",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1500,
        300
      ],
      "notes": "\u9501\u5b9a\u5173\u952e\u8bcd\u72b6\u6001\u4e3a\u91c7\u96c6\u4e2d"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ $env.CRAWLER_API_URL + '/api/search/human' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-API-Key",
              "value": "={{ $env.CRAWLER_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"keyword\": \"{{ $node['Extract Keywords'].json.keyword }}\",\n  \"limit\": {{ $node['Extract Keywords'].json.crawl_limit }}\n}",
        "options": {
          "timeout": 60000
        }
      },
      "id": "search-xhs",
      "name": "Search XHS",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1750,
        300
      ],
      "notes": "\u8c03\u7528\u722c\u866bAPI\u641c\u7d22\u5c0f\u7ea2\u4e66"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "check-success",
              "leftValue": "={{ $json.success }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "check-search-result",
      "name": "Search Success?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2000,
        300
      ],
      "notes": "\u68c0\u67e5\u641c\u7d22\u662f\u5426\u6210\u529f"
    },
    {
      "parameters": {
        "jsCode": "// \u8f6c\u6362\u641c\u7d22\u7ed3\u679c\u4e3a\u98de\u4e66\u8bb0\u5f55\u683c\u5f0f\nconst searchResult = $input.first().json;\nconst keywordData = $node['Extract Keywords'].json;\nconst items = searchResult.data?.items || [];\nconst minLikes = keywordData.min_likes || 0;\n\n// \u8fc7\u6ee4\u4f4e\u4e8e\u6700\u4f4e\u70b9\u8d5e\u6570\u7684\u7b14\u8bb0\nconst filteredItems = items.filter(item => {\n  const likes = parseInt(item.likes) || 0;\n  return likes >= minLikes;\n});\n\n// \u8f6c\u6362\u4e3a\u98de\u4e66\u8bb0\u5f55\u683c\u5f0f\nconst records = filteredItems.map(item => ({\n  fields: {\n    keyword_id: keywordData.record_id,\n    note_id: item.note_id,\n    title: item.title || '',\n    author: item.author || '',\n    likes: parseInt(item.likes) || 0,\n    cover_url: item.cover || '',\n    status: '\u5f85\u63d0\u53d6',\n    crawled_at: new Date().toISOString()\n  }\n}));\n\nreturn [{ json: { records, keyword_record_id: keywordData.record_id, total: records.length } }];"
      },
      "id": "transform-results",
      "name": "Transform Results",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2250,
        200
      ],
      "notes": "\u8f6c\u6362\u641c\u7d22\u7ed3\u679c\u683c\u5f0f"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "={{ 'https://open.feishu.cn/open-apis/bitable/v1/apps/' + $env.LARK_APP_TOKEN + '/tables/' + $env.LARK_TOPICS_TABLE_ID + '/records/batch_create' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['Get Lark Token'].json.tenant_access_token }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ records: $json.records }) }}",
        "options": {}
      },
      "id": "save-to-topics",
      "name": "Save to Topics",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2500,
        200
      ],
      "notes": "\u6279\u91cf\u5199\u5165Topics\u8868"
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{ 'https://open.feishu.cn/open-apis/bitable/v1/apps/' + $env.LARK_APP_TOKEN + '/tables/' + $env.LARK_KEYWORDS_TABLE_ID + '/records/' + $json.keyword_record_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['Get Lark Token'].json.tenant_access_token }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"fields\": {\n    \"status\": \"\u5df2\u91c7\u96c6\",\n    \"last_crawl_time\": \"{{ $now.toISO() }}\",\n    \"locked_at\": null\n  }\n}",
        "options": {}
      },
      "id": "update-keyword-success",
      "name": "Update Keyword Success",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2750,
        200
      ],
      "notes": "\u66f4\u65b0\u5173\u952e\u8bcd\u72b6\u6001\u4e3a\u5df2\u91c7\u96c6"
    },
    {
      "parameters": {
        "method": "PATCH",
        "url": "={{ 'https://open.feishu.cn/open-apis/bitable/v1/apps/' + $env.LARK_APP_TOKEN + '/tables/' + $env.LARK_KEYWORDS_TABLE_ID + '/records/' + $node['Extract Keywords'].json.record_id }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "=Bearer {{ $node['Get Lark Token'].json.tenant_access_token }}"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"fields\": {\n    \"status\": \"\u91c7\u96c6\u5931\u8d25\",\n    \"error_message\": \"{{ $json.error?.message || 'Unknown error' }}\",\n    \"locked_at\": null\n  }\n}",
        "options": {}
      },
      "id": "update-keyword-failed",
      "name": "Update Keyword Failed",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2250,
        400
      ],
      "notes": "\u66f4\u65b0\u5173\u952e\u8bcd\u72b6\u6001\u4e3a\u91c7\u96c6\u5931\u8d25"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { success: true, message: 'Discovery completed', total: $json.total || 0 } }}"
      },
      "id": "respond-success",
      "name": "Respond Success",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        3000,
        200
      ],
      "notes": "\u8fd4\u56de\u6210\u529f\u54cd\u5e94"
    },
    {
      "parameters": {
        "respondWith": "json",
        "responseBody": "={{ { success: false, message: 'No keywords to process' } }}"
      },
      "id": "respond-no-keywords",
      "name": "Respond No Keywords",
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.1,
      "position": [
        1250,
        500
      ],
      "notes": "\u65e0\u5173\u952e\u8bcd\u65f6\u8fd4\u56de"
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Get Lark Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook Trigger": {
      "main": [
        [
          {
            "node": "Get Lark Token",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Lark Token": {
      "main": [
        [
          {
            "node": "Query Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Query Keywords": {
      "main": [
        [
          {
            "node": "Has Keywords?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Has Keywords?": {
      "main": [
        [
          {
            "node": "Extract Keywords",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respond No Keywords",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Keywords": {
      "main": [
        [
          {
            "node": "Lock Keyword",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Lock Keyword": {
      "main": [
        [
          {
            "node": "Search XHS",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search XHS": {
      "main": [
        [
          {
            "node": "Search Success?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Search Success?": {
      "main": [
        [
          {
            "node": "Transform Results",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Update Keyword Failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transform Results": {
      "main": [
        [
          {
            "node": "Save to Topics",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save to Topics": {
      "main": [
        [
          {
            "node": "Update Keyword Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Keyword Success": {
      "main": [
        [
          {
            "node": "Respond Success",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "staticData": null,
  "tags": [
    {
      "name": "XHS-Pipeline"
    }
  ],
  "triggerCount": 2
}