Node.js 接入淘宝 API 实战:构建商品信息实时同步服务

在电商系统开发中,实时获取和同步商品信息是核心需求之一。本文将详细介绍如何使用 Node.js 接入淘宝 API,构建一个商品信息实时同步服务,帮助开发者快速实现与淘宝生态的数据对接。

前期准备

1. 开放账号配置

首先需要完成以下操作:

  • 注册并完成认证
  • 获取 Api Key 和 Api Secret
  • 申请所需 API 的调用权限(如商品详情查询 api)
  • 配置 IP 白名单(提高接口调用安全性)

2. 技术栈选择

  • 核心语言:Node.js (v14+)
  • 包管理:npm/yarn
  • 主要依赖:
    • axios:处理 HTTP 请求
    • crypto-js:实现签名算法
    • dotenv:管理环境变量
    • node-schedule:定时任务调度
    • mongoose:MongoDB 数据库操作(可选)

实现步骤

1. 项目初始化

复制代码
mkdir taobao-api-service
cd taobao-api-service
npm init -y
npm install axios crypto-js dotenv node-schedule mongoose

2. 配置环境变量

创建.env文件:

复制代码
# 淘宝API配置
APP_KEY=你的AppKey
APP_SECRET=你的AppSecret
API_GATEWAY=https://eco.taobao.com/router/rest

# 数据库配置(可选)
MONGO_URI=mongodb://localhost:27017/taobao_products

# 定时任务配置(分钟)
SYNC_INTERVAL=30

3. 淘宝 API 签名工具实现

创建taobao-sign.js

复制代码
const CryptoJS = require('crypto-js');

/**
 * 生成淘宝API签名
 * @param {Object} params 请求参数
 * @param {String} appSecret 应用密钥
 * @returns {String} 签名结果
 */
function generateSign(params, appSecret) {
  // 1. 按键名排序
  const sortedParams = Object.keys(params)
    .sort()
    .reduce((obj, key) => {
      obj[key] = params[key];
      return obj;
    }, {});
  
  // 2. 拼接参数
  let signStr = appSecret;
  for (const key in sortedParams) {
    if (sortedParams.hasOwnProperty(key) && sortedParams[key] !== '') {
      signStr += `${key}${sortedParams[key]}`;
    }
  }
  signStr += appSecret;
  
  // 3. 计算MD5并转为大写
  return CryptoJS.MD5(signStr).toString().toUpperCase();
}

module.exports = { generateSign };

4. 淘宝 API 客户端实现

创建taobao-client.js

复制代码
const axios = require('axios');
const { generateSign } = require('./taobao-sign');
require('dotenv').config();

const { APP_KEY, APP_SECRET, API_GATEWAY } = process.env;

/**
 * 淘宝API客户端
 */
class TaobaoClient {
  constructor() {
    this.appKey = APP_KEY;
    this.appSecret = APP_SECRET;
    this.gateway = API_GATEWAY;
  }
  
  /**
   * 通用API调用方法
   * @param {String} method API方法名
   * @param {Object} params 业务参数
   * @returns {Promise<Object>} API返回结果
   */
  async invoke(method, params = {}) {
    // 公共参数
    const publicParams = {
      app_key: this.appKey,
      method,
      format: 'json',
      v: '2.0',
      timestamp: new Date().toISOString().replace('T', ' ').split('.')[0],
      sign_method: 'md5'
    };
    
    // 合并参数
    const allParams = { ...publicParams, ...params };
    
    // 生成签名
    allParams.sign = generateSign(allParams, this.appSecret);
    
    try {
      const response = await axios.post(this.gateway, null, { params: allParams });
      
      if (response.data.error_response) {
        throw new Error(`API错误: ${response.data.error_response.msg} (${response.data.error_response.code})`);
      }
      
      return response.data;
    } catch (error) {
      console.error(`API调用失败: ${error.message}`);
      throw error;
    }
  }
  
  /**
   * 获取商品详情
   * @param {String} numIid 商品ID
   * @returns {Promise<Object>} 商品详情
   */
  async getProductDetail(numIid) {
    return this.invoke('taobao.item.get', {
      num_iid: numIid,
      fields: 'num_iid,title,price,promotion_price,stock,desc,pics'
    });
  }
  
  /**
   * 批量获取商品信息
   * @param {Array<String>} numIids 商品ID列表
   * @returns {Promise<Object>} 商品列表信息
   */
  async getProductList(numIids) {
    return this.invoke('taobao.items.list.get', {
      num_iids: numIids.join(','),
      fields: 'num_iid,title,price,promotion_price,stock'
    });
  }
}

module.exports = new TaobaoClient();

5. 数据存储模型(可选)

创建models/Product.js

复制代码
const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
  numIid: { type: String, required: true, unique: true },
  title: String,
  price: Number,
  promotionPrice: Number,
  stock: Number,
  desc: String,
  pics: [String],
  updatedAt: { type: Date, default: Date.now }
});

// 更新时间自动维护
productSchema.pre('save', function(next) {
  this.updatedAt = Date.now();
  next();
});

module.exports = mongoose.model('Product', productSchema);

6. 同步服务实现

创建sync-service.js

复制代码
const taobaoClient = require('./taobao-client');
const Product = require('./models/Product');
const schedule = require('node-schedule');
require('dotenv').config();

const { SYNC_INTERVAL } = process.env;

/**
 * 同步单个商品信息
 * @param {String} numIid 商品ID
 */
async function syncProduct(numIid) {
  try {
    console.log(`开始同步商品: ${numIid}`);
    
    const result = await taobaoClient.getProductDetail(numIid);
    const productData = result.item_get_response.item;
    
    // 转换数据格式
    const productInfo = {
      numIid: productData.num_iid,
      title: productData.title,
      price: parseFloat(productData.price),
      promotionPrice: productData.promotion_price ? parseFloat(productData.promotion_price) : null,
      stock: parseInt(productData.stock),
      desc: productData.desc,
      pics: productData.pics ? productData.pics.split(',') : []
    };
    
    // 保存到数据库
    await Product.findOneAndUpdate(
      { numIid: productInfo.numIid },
      productInfo,
      { upsert: true, new: true }
    );
    
    console.log(`商品同步成功: ${numIid}`);
    return productInfo;
  } catch (error) {
    console.error(`商品同步失败 ${numIid}: ${error.message}`);
  }
}

/**
 * 批量同步商品
 * @param {Array<String>} productIds 商品ID列表
 */
async function batchSyncProducts(productIds) {
  console.log(`开始批量同步 ${productIds.length} 个商品`);
  
  try {
    // 分批处理,避免超过API限制
    const batchSize = 20;
    for (let i = 0; i < productIds.length; i += batchSize) {
      const batch = productIds.slice(i, i + batchSize);
      await Promise.all(batch.map(id => syncProduct(id)));
      // 避免请求过于频繁
      await new Promise(resolve => setTimeout(resolve, 1000));
    }
    
    console.log(`批量同步完成`);
  } catch (error) {
    console.error(`批量同步失败: ${error.message}`);
  }
}

/**
 * 启动定时同步任务
 * @param {Array<String>} productIds 需要同步的商品ID列表
 */
function startSyncSchedule(productIds) {
  console.log(`启动定时同步任务,间隔 ${SYNC_INTERVAL} 分钟`);
  
  // 立即执行一次
  batchSyncProducts(productIds);
  
  // 设置定时任务
  schedule.scheduleJob(`*/${SYNC_INTERVAL} * * * *`, () => {
    console.log(`定时任务触发: ${new Date().toLocaleString()}`);
    batchSyncProducts(productIds);
  });
}

module.exports = {
  syncProduct,
  batchSyncProducts,
  startSyncSchedule
};

7. 主程序入口

创建index.js

复制代码
const mongoose = require('mongoose');
const { startSyncSchedule } = require('./sync-service');
require('dotenv').config();

const { MONGO_URI } = process.env;

// 需要同步的商品ID列表
const PRODUCT_IDS = [
  '123456789',  // 替换为实际商品ID
  '987654321',
  // 可以添加更多商品ID
];

// 连接数据库
mongoose.connect(MONGO_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true
})
.then(() => {
  console.log('数据库连接成功');
  // 启动同步服务
  startSyncSchedule(PRODUCT_IDS);
})
.catch(err => {
  console.error('数据库连接失败:', err);
  process.exit(1);
});

// 处理未捕获的异常
process.on('unhandledRejection', (reason, promise) => {
  console.error('未处理的Promise拒绝:', reason);
});

process.on('uncaughtException', (error) => {
  console.error('未捕获的异常:', error);
});

服务运行与测试

  1. 确保 MongoDB 服务已启动(如果使用数据库存储)

  2. 替换.env文件中的配置信息和index.js中的商品 ID 列表

  3. 启动服务:

    node index.js

服务启动后,会立即执行一次商品同步,之后按照设定的时间间隔(默认 30 分钟)自动同步商品信息。

优化与扩展建议

  1. 错误重试机制:为 API 调用添加重试逻辑,提高稳定性
  2. 请求频率控制:实现更精细的限流策略,避免触发淘宝 API 的频率限制
  3. 日志系统:集成 winston 等日志库,完善日志记录
  4. 监控告警:添加健康检查和异常告警机制
  5. 缓存策略:对频繁访问的商品信息添加缓存,减少 API 调用次数
  6. 分布式部署:在高并发场景下,可考虑将服务拆分为生产者 - 消费者模式

注意事项

  1. 淘宝 API 有调用次数限制,需合理规划调用频率
  2. 签名算法需严格按照官方文档实现,否则会导致调用失败
  3. 敏感信息(如 App Secret)需妥善保管,避免泄露
  4. 生产环境中建议添加接口调用监控和告警机制
  5. 遵守淘宝开放平台的使用规范,避免违规操作

通过本文的实现,你可以快速搭建一个稳定可靠的淘宝商品信息同步服务,为电商系统提供实时的商品数据支持。根据实际业务需求,还可以扩展更多功能,如订单同步、评价获取等。

相关推荐
小白用python13 小时前
win10安装nodejs及better-sqlite3失败的解决方案
node.js·better-sqlite3
ikoala19 小时前
Node.js 25 正式发布:性能飙升、安全升级、全面向 Web 靠拢!
前端·面试·node.js
前端架构师-老李1 天前
npm、yarn、pnpm的对比和优略
前端·npm·node.js·pnpm·yarn
www_stdio1 天前
用 n8n 打造科技新闻速览工作流:从搭建到落地的完整指南
node.js
gustt1 天前
每天一杯科技资讯咖啡,用 n8n 喂给 AI
人工智能·node.js
烤红薯2091 天前
如何使用代理IP发送请求(711Proxy实战指南)
node.js
EndingCoder1 天前
Node.js SQL数据库:MySQL/PostgreSQL集成
javascript·数据库·sql·mysql·postgresql·node.js
fruge1 天前
Node.js 入门与实战:从零构建用户管理系统
node.js