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);
});
服务运行与测试
- 确保 MongoDB 服务已启动(如果使用数据库存储)
- 替换
.env
文件中的配置信息和index.js
中的商品 ID 列表 - 启动服务:
node index.js
服务启动后,会立即执行一次商品同步,之后按照设定的时间间隔(默认 30 分钟)自动同步商品信息。
优化与扩展建议
- 错误重试机制:为 API 调用添加重试逻辑,提高稳定性
- 请求频率控制:实现更精细的限流策略,避免触发淘宝 API 的频率限制
- 日志系统:集成 winston 等日志库,完善日志记录
- 监控告警:添加健康检查和异常告警机制
- 缓存策略:对频繁访问的商品信息添加缓存,减少 API 调用次数
- 分布式部署:在高并发场景下,可考虑将服务拆分为生产者 - 消费者模式
注意事项
- 淘宝 API 有调用次数限制,需合理规划调用频率
- 签名算法需严格按照官方文档实现,否则会导致调用失败
- 敏感信息(如 App Secret)需妥善保管,避免泄露
- 生产环境中建议添加接口调用监控和告警机制
- 遵守淘宝开放平台的使用规范,避免违规操作
通过本文的实现,你可以快速搭建一个稳定可靠的淘宝商品信息同步服务,为电商系统提供实时的商品数据支持。根据实际业务需求,还可以扩展更多功能,如订单同步、评价获取等。