{
  "id": "RtubOFIkhPZ0otPu",
  "name": "Get Taobao and Tmall product reviews with JustOneAPI",
  "tags": [],
  "nodes": [
    {
      "id": "dec03413-dfcf-4832-83e1-930a70edb1fb",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2704,
        112
      ],
      "parameters": {
        "width": 480,
        "height": 528,
        "content": "## Get Taobao and Tmall product reviews with JustOneAPI\n\n### How it works\n\n1. The workflow is triggered manually.\n2. API and review parameters are set.\n3. Taobao/Tmall product reviews are fetched via HTTP request.\n4. The raw review response is stored.\n5. The product reviews output is built.\n6. The final product reviews are stored.\n\n### Setup steps\n\n- - [ ] Configure the manual trigger node to start the workflow.\n- [ ] Set the API and review parameters in the 'Set API and Review Parameters' node.\n- [ ] Ensure the HTTP request node has the correct endpoint for fetching Taobao/Tmall product reviews.\n- [ ] Configure the 'Store Raw Review Response' node to save the raw data.\n- [ ] Write the necessary code in the 'Build Product Reviews Output' node to process the reviews.\n- [ ] Set up the 'Store Final Product Reviews' node to save the processed reviews.\n"
      },
      "typeVersion": 1
    },
    {
      "id": "627b7a01-7777-4077-86cc-e46c7398810f",
      "name": "Sticky Note1",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3264,
        112
      ],
      "parameters": {
        "color": 7,
        "height": 96,
        "content": "## Workflow Trigger\n\nManually initiates the workflow."
      },
      "typeVersion": 1
    },
    {
      "id": "b17efa11-d515-4e48-9531-33eba4677d3d",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3536,
        112
      ],
      "parameters": {
        "color": 7,
        "height": 112,
        "content": "## Parameter Setup\n\nConfigures API and review parameters for the request."
      },
      "typeVersion": 1
    },
    {
      "id": "ce69c994-f0e9-493f-ab8c-f32c8b0e794e",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        3824,
        112
      ],
      "parameters": {
        "color": 7,
        "width": 256,
        "height": 96,
        "content": "## Fetch Reviews\n\nFetches Taobao/Tmall product reviews via HTTP request."
      },
      "typeVersion": 1
    },
    {
      "id": "81ac17fe-d8c2-4e18-9df7-a0b574c1411a",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4160,
        112
      ],
      "parameters": {
        "color": 7,
        "height": 96,
        "content": "## Store Raw Data\n\nStores the raw response from the API request."
      },
      "typeVersion": 1
    },
    {
      "id": "dd23e595-827a-404e-a7cc-82378693f487",
      "name": "Sticky Note5",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4496,
        112
      ],
      "parameters": {
        "color": 7,
        "height": 112,
        "content": "## Build Output\n\nProcesses and builds the product reviews output."
      },
      "typeVersion": 1
    },
    {
      "id": "e184bfff-d1de-467c-bbef-e57c70e7e3e0",
      "name": "Sticky Note6",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        4960,
        112
      ],
      "parameters": {
        "color": 7,
        "height": 96,
        "content": "## Store Final Data\n\nStores the final processed product reviews."
      },
      "typeVersion": 1
    },
    {
      "id": "e1768d81-e10a-42a7-ad37-cc5671862ff0",
      "name": "Manual Execution Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        3312,
        272
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "8709d111-a362-471c-aeab-64a7780b0ec6",
      "name": "Set API and Review Parameters",
      "type": "n8n-nodes-base.set",
      "position": [
        3584,
        272
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "{\n  \"baseUrl\": \"https://api.justoneapi.com\",\n  \"token\": \"YOUR_JUSTONEAPI_TOKEN\",\n  \"itemId\": \"TARGET_TAOBAO_ITEM_ID\",\n  \"orderType\": \"feedbackdate\",\n  \"page\": 1\n}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "7f2a414b-63cc-42f8-bca4-e4523feeb719",
      "name": "Fetch Taobao/Tmall Reviews via API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        3872,
        272
      ],
      "parameters": {
        "url": "={{$json.baseUrl + '/api/taobao/get-item-comment/v3'}}",
        "options": {
          "redirect": {
            "redirect": {}
          }
        },
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "token",
              "value": "={{$json.token}}"
            },
            {
              "name": "itemId",
              "value": "={{$json.itemId}}"
            },
            {
              "name": "orderType",
              "value": "={{$json.orderType}}"
            },
            {
              "name": "page",
              "value": "={{$json.page}}"
            }
          ]
        }
      },
      "typeVersion": 4.2
    },
    {
      "id": "8fd9e356-dae4-41e7-993d-f0bdd8205c6a",
      "name": "Store Raw Review Data",
      "type": "n8n-nodes-base.set",
      "position": [
        4208,
        496
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={{$json}}"
      },
      "typeVersion": 3.4
    },
    {
      "id": "2b7147ba-027b-4066-8585-d507ab1829ef",
      "name": "Process Review Data",
      "type": "n8n-nodes-base.code",
      "position": [
        4544,
        272
      ],
      "parameters": {
        "jsCode": "const cfg = $('Set API and Review Parameters').first().json;\nconst rows = $input.all();\n\nfunction cleanUrl(value) {\n  if (!value) return '';\n  const text = String(value);\n  if (text.startsWith('//')) return `https:${text}`;\n  return text;\n}\n\nfunction cleanPhoto(photo) {\n  return {\n    thumbnail: cleanUrl(photo.thumbnail || ''),\n    url: cleanUrl(photo.url || ''),\n    fileId: photo.fileId ?? '',\n    receiveId: photo.receiveId ?? '',\n    status: photo.status ?? ''\n  };\n}\n\nfunction cleanAppend(append) {\n  if (!append) return null;\n\n  return {\n    appendId: append.appendId ?? '',\n    show: append.show ?? false,\n    dayAfterConfirm: append.dayAfterConfirm ?? null,\n    content: append.content || '',\n    photos: Array.isArray(append.photos) ? append.photos.map(cleanPhoto) : [],\n    videos: Array.isArray(append.videos) ? append.videos : [],\n    reply: append.reply ?? null,\n    vicious: append.vicious || ''\n  };\n}\n\nreturn rows.map(item => {\n  const root = item.json || {};\n  const data = root.data || {};\n  const comments = Array.isArray(data.comments) ? data.comments : [];\n\n  const cleanedComments = comments.map(comment => {\n    const user = comment.user || {};\n    const auction = comment.auction || {};\n    const shareInfo = comment.shareInfo || {};\n\n    return {\n      rateId: comment.rateId ?? '',\n      date: comment.date || '',\n      content: comment.content || '',\n      rate: comment.rate || '',\n      useful: comment.useful ?? 0,\n      status: comment.status ?? '',\n      validscore: comment.validscore ?? null,\n      propertiesAvg: comment.propertiesAvg || '',\n      dayAfterConfirm: comment.dayAfterConfirm ?? null,\n      from: comment.from || '',\n      tag: comment.tag || '',\n      noQna: comment.noQna ?? false,\n      photoCount: Array.isArray(comment.photos) ? comment.photos.length : 0,\n      photos: Array.isArray(comment.photos) ? comment.photos.map(cleanPhoto) : [],\n      video: comment.video ?? null,\n      reply: comment.reply ?? null,\n      append: cleanAppend(comment.append),\n      appendList: Array.isArray(comment.appendList) ? comment.appendList.map(cleanAppend).filter(Boolean) : [],\n      auction: {\n        aucNumId: auction.aucNumId || '',\n        sku: auction.sku || '',\n        title: auction.title || '',\n        link: cleanUrl(auction.link || ''),\n        auctionPic: cleanUrl(auction.auctionPic || ''),\n        thumbnail: cleanUrl(auction.thumbnail || '')\n      },\n      user: {\n        nick: user.nick || '',\n        vipLevel: user.vipLevel ?? null,\n        displayRatePic: user.displayRatePic || '',\n        creditPic: cleanUrl(user.creditPic || ''),\n        anony: user.anony ?? false,\n        rank: user.rank ?? null,\n        avatar: cleanUrl(user.avatar || ''),\n        vip: user.vip || '',\n        userId: user.userId || '',\n        nickUrl: cleanUrl(user.nickUrl || ''),\n        rankUrl: cleanUrl(user.rankUrl || '')\n      },\n      shareInfo: {\n        lastReplyTime: shareInfo.lastReplyTime || '',\n        share: shareInfo.share ?? false,\n        pic: shareInfo.pic ?? 0,\n        reply: shareInfo.reply ?? 0,\n        userNumIdBase64: shareInfo.userNumIdBase64 || ''\n      },\n      raw: comment\n    };\n  });\n\n  const photoReviewCount = cleanedComments.filter(comment => comment.photoCount > 0).length;\n  const appendReviewCount = cleanedComments.filter(comment => comment.append || comment.appendList.length > 0).length;\n  const reviewsWithTextCount = cleanedComments.filter(comment => comment.content && comment.content !== '\u6b64\u7528\u6237\u6ca1\u6709\u586b\u5199\u8bc4\u4ef7\u3002').length;\n\n  return {\n    json: {\n      requestedItemId: cfg.itemId || '',\n      requestedOrderType: cfg.orderType || '',\n      requestedPage: cfg.page ?? '',\n      endpoint: '/api/taobao/get-item-comment/v3',\n      responseCode: root.code ?? '',\n      responseMessage: root.message ?? null,\n      recordTime: root.recordTime || '',\n      pageInfo: {\n        currentPageNum: data.currentPageNum ?? null,\n        maxPage: data.maxPage ?? null,\n        total: data.total ?? null,\n        totalFuzzy: data.totalFuzzy || '',\n        watershed: data.watershed ?? null,\n        qnaDisabled: data.qnaDisabled ?? null\n      },\n      reviewSummary: {\n        returnedCommentCount: cleanedComments.length,\n        photoReviewCount,\n        appendReviewCount,\n        reviewsWithTextCount\n      },\n      comments: cleanedComments,\n      raw: root\n    }\n  };\n});\n"
      },
      "typeVersion": 2
    },
    {
      "id": "7b9239fb-0b6b-42c6-8b6d-d0be0f2ee52b",
      "name": "Store Final Reviews",
      "type": "n8n-nodes-base.set",
      "position": [
        5024,
        272
      ],
      "parameters": {
        "mode": "raw",
        "options": {},
        "jsonOutput": "={{$json}}"
      },
      "typeVersion": 3.4
    }
  ],
  "active": false,
  "settings": {
    "binaryMode": "separate",
    "executionOrder": "v1"
  },
  "versionId": "7bc60be1-bff1-4336-9834-6964fdaf9772",
  "connections": {
    "Process Review Data": {
      "main": [
        [
          {
            "node": "Store Final Reviews",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manual Execution Trigger": {
      "main": [
        [
          {
            "node": "Set API and Review Parameters",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set API and Review Parameters": {
      "main": [
        [
          {
            "node": "Fetch Taobao/Tmall Reviews via API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch Taobao/Tmall Reviews via API": {
      "main": [
        [
          {
            "node": "Store Raw Review Data",
            "type": "main",
            "index": 0
          },
          {
            "node": "Process Review Data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}