# 前端 API 调用示例文档
本项目后端提供三个平台的 API 代理服务,统一挂载在 /api/ 路径下:
| 平台 | 路径前缀 | 说明 |
|---|---|---|
| Amazon SP-API | /api/amazon/ |
亚马逊卖家平台 API,涵盖订单、商品、销售、目录等模块 |
| Sorftime API | /api/sorftime/ |
Sorftime 数据分析平台,提供类目、产品、关键词、监控等功能 |
| TikHub API | /api/tikhub/ |
TikTok 数据平台,提供用户、视频、电商、广告等数据查询 |
⭐ 标记表示常用接口,建议优先了解。
https://server-msq.fmode.cn
本地开发环境可使用
http://localhost:10003,生产环境统一使用 HTTPS 域名。
| 平台 | 认证 Header | 必填 | 说明 |
|---|---|---|---|
| Amazon SP-API | shop-objectId |
✅ | Parse 数据库中 Shop 对象的 ObjectId,后端通过它查询店铺的 SP-API 凭证 |
| Sorftime API | 无需额外认证 | — | 后端已内置 API Token |
| TikHub API | 无需额外认证 | — | 后端已内置 API Key |
所有接口统一返回 JSON 格式,通用结构如下:
interface ApiResponse<T = any> {
success: boolean; // 是否成功
data: T; // 业务数据
timestamp: string; // 时间戳
}
建议在项目中封装统一的请求工具,以下为 axios 封装示例:
import axios, { AxiosInstance } from 'axios';
const BASE_URL = 'https://server-msq.fmode.cn';
// Amazon 请求实例
export const amazonClient: AxiosInstance = axios.create({
baseURL: `${BASE_URL}/api/amazon`,
headers: { 'Content-Type': 'application/json' },
});
// 设置店铺 ObjectId(Parse 数据库中 Shop 对象的 ObjectId)
export function setShopObjectId(shopObjectId: string): void {
amazonClient.defaults.headers.common['shop-objectId'] = shopObjectId;
}
// Sorftime 请求实例(后端已内置 Token,无需前端传入)
export const sorftimeClient: AxiosInstance = axios.create({
baseURL: `${BASE_URL}/api/sorftime`,
headers: { 'Content-Type': 'application/json' },
});
// TikHub 请求实例(后端已内置 API Key,无需前端传入)
export const tikhubClient: AxiosInstance = axios.create({
baseURL: `${BASE_URL}/api/tikhub`,
headers: { 'Content-Type': 'application/json' },
});
amazonClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response) {
const status = error.response.status;
switch (status) {
case 400: console.error('请求参数错误'); break;
case 401: console.error('认证失败,请检查 shop-objectId'); break;
case 403: console.error('权限不足'); break;
case 429: console.error('请求过于频繁,请稍后重试'); break;
case 500: console.error('服务器内部错误'); break;
}
}
return Promise.reject(error);
}
);
所有 Amazon 接口均需在请求头中携带
shop-objectId(Parse 数据库中 Shop 对象的 ObjectId)。 测试接口/test除外,无需认证。
GET /api/amazon/test// fetch
const res = await fetch('https://server-msq.fmode.cn/api/amazon/test');
const data = await res.json();
// axios
const { data } = await amazonClient.get('/test');
响应示例:
{
"success": true,
"data": { "message": "SP-API 路由测试成功" },
"timestamp": "2025-01-01T12:00:00.000Z"
}
GET /api/amazon/customerFeedback/items/:asin/reviews/topics — 商品评论主题// fetch
const res = await fetch('https://server-msq.fmode.cn/api/amazon/customerFeedback/items/B0EXAMPLE1/reviews/topics', {
headers: { 'shop-objectId': 'your-shop-object-id' }
});
// axios
const { data } = await amazonClient.get('/customerFeedback/items/B0EXAMPLE1/reviews/topics');
GET /api/amazon/customerFeedback/items/:asin/browseNode — 商品浏览节点const { data } = await amazonClient.get('/customerFeedback/items/B0EXAMPLE1/browseNode');
GET /api/amazon/customerFeedback/browseNodes/:browseNodeId/reviews/topics — 浏览节点评论主题const { data } = await amazonClient.get('/customerFeedback/browseNodes/12345/reviews/topics');
GET /api/amazon/customerFeedback/items/:asin/reviews/trends — 商品评论趋势const { data } = await amazonClient.get('/customerFeedback/items/B0EXAMPLE1/reviews/trends');
GET /api/amazon/customerFeedback/browseNodes/:browseNodeId/reviews/trends — 浏览节点评论趋势const { data } = await amazonClient.get('/customerFeedback/browseNodes/12345/reviews/trends');
GET /api/amazon/customerFeedback/browseNodes/:browseNodeId/returns/topics — 浏览节点退货主题const { data } = await amazonClient.get('/customerFeedback/browseNodes/12345/returns/topics');
GET /api/amazon/customerFeedback/browseNodes/:browseNodeId/returns/trends — 浏览节点退货趋势const { data } = await amazonClient.get('/customerFeedback/browseNodes/12345/returns/trends');
GET /api/amazon/orders — 获取订单列表// fetch
const res = await fetch('https://server-msq.fmode.cn/api/amazon/orders?MarketplaceIds=ATVPDKIKX0DER&CreatedAfter=2025-01-01T00:00:00Z', {
headers: { 'shop-objectId': 'your-shop-object-id' }
});
// axios
const { data } = await amazonClient.get('/orders', {
params: {
MarketplaceIds: 'ATVPDKIKX0DER',
CreatedAfter: '2025-01-01T00:00:00Z',
CreatedBefore: '2025-01-31T23:59:59Z',
OrderStatuses: 'Shipped'
}
});
GET /api/amazon/orders/:orderId — 获取订单详情const { data } = await amazonClient.get('/orders/111-1234567-1234567');
GET /api/amazon/orders/:orderId/items — 获取订单商品明细const { data } = await amazonClient.get('/orders/111-1234567-1234567/items', {
params: { NextToken: 'optional-pagination-token' }
});
GET /api/amazon/sales/orderMetrics — 获取销售指标// fetch
const res = await fetch('https://server-msq.fmode.cn/api/amazon/sales/orderMetrics?marketplaceIds=ATVPDKIKX0DER&interval=2025-01-01T00:00:00Z--2025-01-31T23:59:59Z&granularity=Day', {
headers: { 'shop-objectId': 'your-shop-object-id' }
});
// axios
const { data } = await amazonClient.get('/sales/orderMetrics', {
params: {
marketplaceIds: 'ATVPDKIKX0DER',
interval: '2025-01-01T00:00:00Z--2025-01-31T23:59:59Z',
granularity: 'Day'
}
});
GET /api/amazon/listings/items/:sellerId — 搜索卖家 Listing 列表// fetch
const res = await fetch('https://server-msq.fmode.cn/api/amazon/listings/items/A1EXAMPLE?marketplaceIds=ATVPDKIKX0DER', {
headers: { 'shop-objectId': 'your-shop-object-id' }
});
// axios
const { data } = await amazonClient.get('/listings/items/A1EXAMPLE', {
params: {
marketplaceIds: 'ATVPDKIKX0DER',
pageSize: 20
}
});
GET /api/amazon/listings/items/:sellerId/:sku — 获取单个 Listing 详情const { data } = await amazonClient.get('/listings/items/A1EXAMPLE/WH-BT-001', {
params: { marketplaceIds: 'ATVPDKIKX0DER' }
});
PUT /api/amazon/listings/items/:sellerId/:sku — 更新 Listingconst { data } = await amazonClient.put('/listings/items/A1EXAMPLE/WH-BT-001', {
productType: 'HEADPHONES',
attributes: {
item_name: [{ value: 'Updated Product Title' }]
}
}, {
params: { marketplaceIds: 'ATVPDKIKX0DER' }
});
DELETE /api/amazon/listings/items/:sellerId/:sku — 删除 Listingconst { data } = await amazonClient.delete('/listings/items/A1EXAMPLE/WH-BT-001', {
params: { marketplaceIds: 'ATVPDKIKX0DER' }
});
GET /api/amazon/externalFulfillment/returns — 获取退货列表const { data } = await amazonClient.get('/externalFulfillment/returns', {
params: {
MarketplaceId: 'ATVPDKIKX0DER',
Status: 'Open'
}
});
GET /api/amazon/externalFulfillment/returns/:returnId — 获取退货详情const { data } = await amazonClient.get('/externalFulfillment/returns/RET-12345');
GET /api/amazon/catalog/categories — 获取目录分类const { data } = await amazonClient.get('/catalog/categories', {
params: {
MarketplaceId: 'ATVPDKIKX0DER',
ASIN: 'B0EXAMPLE1'
}
});
GET /api/amazon/catalog/items — 搜索目录商品// fetch
const res = await fetch('https://server-msq.fmode.cn/api/amazon/catalog/items?keywords=wireless+earbuds&marketplaceIds=ATVPDKIKX0DER', {
headers: { 'shop-objectId': 'your-shop-object-id' }
});
// axios
const { data } = await amazonClient.get('/catalog/items', {
params: {
keywords: 'wireless earbuds',
marketplaceIds: 'ATVPDKIKX0DER',
includedData: 'summaries,images,salesRanks'
}
});
GET /api/amazon/catalog/items/:asin — 获取商品详情const { data } = await amazonClient.get('/catalog/items/B0EXAMPLE1', {
params: {
marketplaceIds: 'ATVPDKIKX0DER',
includedData: 'summaries,images,salesRanks,dimensions,relationships'
}
});
GET /api/amazon/sellers/account — 获取卖家账户信息const { data } = await amazonClient.get('/sellers/account');
GET /api/amazon/sellers/marketplaceParticipations — 获取市场参与信息const { data } = await amazonClient.get('/sellers/marketplaceParticipations');
通用转发接口可以调用任意亚马逊 SP-API 端点,适用于上述封装接口未覆盖的场景。支持 functionId 参数触发 Parse 云函数对返回数据做后处理。
POST /api/amazon/forward请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | ✅ | SP-API 路径,如 /orders/v0/orders |
| method | string | 否 | HTTP 方法,默认 GET |
| query | object | 否 | 查询参数 |
| body | object | 否 | 请求体 |
| functionName | string | 否 | 自定义转换函数名 |
| functionId | string | 否 | Parse 云函数 ID,提供后会执行云函数处理返回数据 |
// 基本用法 — 获取订单
const { data } = await amazonClient.post('/forward', {
path: '/orders/v0/orders',
method: 'GET',
query: {
MarketplaceIds: 'ATVPDKIKX0DER',
CreatedAfter: '2025-01-01T00:00:00Z'
}
});
// 带云函数后处理 — 获取订单并执行云函数清洗数据
const { data: processed } = await amazonClient.post('/forward', {
path: '/orders/v0/orders',
method: 'GET',
query: { MarketplaceIds: 'ATVPDKIKX0DER' },
functionId: 'RFDDkJ2usG'
});
// 响应包含 { apiResult: {...}, cloudFunctionResult: {...} }
Angular HttpClient 调用示例:
// Angular 中的典型调用方式
this.http.post('https://server-msq.fmode.cn/api/amazon/forward', {
path: '/orders/v0/orders/${orderId}',
functionId: 'RFDDkJ2usG'
}, {
headers: { 'shop-objectId': this.shopId }
}).subscribe(res => {
console.log(res);
});
带 functionId 的响应示例:
{
"success": true,
"data": {
"apiResult": { "...亚马逊原始返回..." },
"cloudFunctionResult": { "...云函数处理后的数据..." }
},
"timestamp": "2025-01-01T12:00:00.000Z"
}
Sorftime 的 API Token 已在后端配置,前端无需传入认证信息。 所有 Sorftime 数据查询通过
/api/sorftime/forward转发接口完成。
GET /api/sorftime/test — 测试接口const res = await fetch('https://server-msq.fmode.cn/api/sorftime/test');
const data = await res.json();
// { "message": "Sorftime API Router Loaded Successfully v0.0.1" }
GET /api/sorftime/health — 健康检查const res = await fetch('https://server-msq.fmode.cn/api/sorftime/health');
const data = await res.json();
// { "service": "sorftime", "timestamp": "2025-01-01T12:00:00.000Z" }
Sorftime 所有数据查询均通过 forward 接口转发。支持 functionId 参数触发 Parse 云函数做后处理。
POST /api/sorftime/forward请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | ✅ | Sorftime API 路径,如 /api/CategoryTree |
| method | string | 否 | HTTP 方法,默认 POST |
| query | object | 否 | 查询参数 |
| body | object | 否 | 请求体 |
| function | string | 否 | 自定义转换函数名 |
| functionId | string | 否 | Parse 云函数 ID |
// axios
const { data } = await sorftimeClient.post('/forward', {
path: '/api/CategoryTree',
body: { domain: 'amazon.com' }
});
// fetch
const res = await fetch('https://server-msq.fmode.cn/api/sorftime/forward', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: '/api/CategoryTree',
body: { domain: 'amazon.com' }
})
});
const { data } = await sorftimeClient.post('/forward', {
path: '/api/ProductQuery',
body: {
domain: 'amazon.com',
keyword: 'wireless earbuds',
page: 1,
pageSize: 20
}
});
const { data } = await sorftimeClient.post('/forward', {
path: '/api/ProductRequest',
body: {
domain: 'amazon.com',
asin: 'B0EXAMPLE1'
}
});
const { data } = await sorftimeClient.post('/forward', {
path: '/api/KeywordQuery',
body: {
domain: 'amazon.com',
keyword: 'wireless earbuds'
}
});
const { data } = await sorftimeClient.post('/forward', {
path: '/api/ASINRequestKeyword',
body: {
domain: 'amazon.com',
asin: 'B0EXAMPLE1'
}
});
const { data } = await sorftimeClient.post('/forward', {
path: '/api/MonitorQuery',
body: {
domain: 'amazon.com',
asin: 'B0EXAMPLE1'
}
});
const { data } = await sorftimeClient.post('/forward', {
path: '/api/ProductQuery',
body: { domain: 'amazon.com', keyword: 'earbuds' },
functionId: 'cloudFuncId123'
});
Angular HttpClient 调用示例:
this.http.post('https://server-msq.fmode.cn/api/sorftime/forward', {
path: '/api/ProductQuery',
body: { domain: 'amazon.com', keyword: 'earbuds' }
}).subscribe(res => {
console.log(res);
});
TikHub 的 API Key 已在后端配置,前端无需传入认证信息。
GET /api/tikhub/healthconst res = await fetch('https://server-msq.fmode.cn/api/tikhub/health');
const data = await res.json();
// { "service": "tikhub", "timestamp": "2025-01-01T12:00:00.000Z" }
通用转发接口可以调用任意 TikHub API 端点。支持 functionId 参数触发 Parse 云函数做后处理。
POST /api/tikhub/forward请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| path | string | ✅ | TikHub API 路径,如 /api/v1/tiktok/web/fetch_user_profile |
| method | string | 否 | HTTP 方法,默认 GET |
| query | object | 否 | 查询参数 |
| body | object | 否 | 请求体 |
| headers | object | 否 | 额外请求头 |
| functionId | string | 否 | Parse 云函数 ID |
// axios
const { data } = await tikhubClient.post('/forward', {
path: '/api/v1/tiktok/web/fetch_user_profile',
method: 'GET',
query: { unique_id: 'example_user' }
});
// fetch
const res = await fetch('https://server-msq.fmode.cn/api/tikhub/forward', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
path: '/api/v1/tiktok/web/fetch_user_profile',
method: 'GET',
query: { unique_id: 'example_user' }
})
});
GET /api/tikhub/tiktok/web/user-profile — 获取用户资料| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| unique_id | string | ✅ | TikTok 用户名 |
// axios
const { data } = await tikhubClient.get('/tiktok/web/user-profile', {
params: { unique_id: 'example_user' }
});
// fetch
const res = await fetch('https://server-msq.fmode.cn/api/tikhub/tiktok/web/user-profile?unique_id=example_user');
GET /api/tikhub/tiktok/web/sec-user-id — 通过链接获取 sec_user_id| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| url | string | ✅ | TikTok 用户主页链接 |
const { data } = await tikhubClient.get('/tiktok/web/sec-user-id', {
params: { url: 'https://www.tiktok.com/@example_user' }
});
GET /api/tikhub/tiktok/app/v3/user-followers — 获取用户粉丝列表| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| user_id | string | ✅ | 用户 ID |
| offset | number | 否 | 偏移量 |
| count | number | 否 | 每页数量 |
const { data } = await tikhubClient.get('/tiktok/app/v3/user-followers', {
params: { user_id: '6789012345', offset: 0, count: 20 }
});
GET /api/tikhub/tiktok/app/v3/user-following — 获取用户关注列表| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| user_id | string | ✅ | 用户 ID |
| offset | number | 否 | 偏移量 |
| count | number | 否 | 每页数量 |
const { data } = await tikhubClient.get('/tiktok/app/v3/user-following', {
params: { user_id: '6789012345', offset: 0, count: 20 }
});
GET /api/tikhub/tiktok/app/v3/video — 获取单个视频详情| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| aweme_id | string | ✅ | 视频 ID |
// axios
const { data } = await tikhubClient.get('/tiktok/app/v3/video', {
params: { aweme_id: '7123456789012345678' }
});
// fetch
const res = await fetch('https://server-msq.fmode.cn/api/tikhub/tiktok/app/v3/video?aweme_id=7123456789012345678');
GET /api/tikhub/tiktok/app/v3/user-repost-videos — 获取用户转发视频| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| user_id | string | ✅ | 用户 ID |
| offset | number | 否 | 偏移量 |
| count | number | 否 | 每页数量 |
const { data } = await tikhubClient.get('/tiktok/app/v3/user-repost-videos', {
params: { user_id: '6789012345', offset: 0, count: 20 }
});
GET /api/tikhub/tiktok/app/v3/video-comments — 获取视频评论| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| aweme_id | string | ✅ | 视频 ID |
| cursor | number | 否 | 分页游标 |
| count | number | 否 | 每页数量 |
const { data } = await tikhubClient.get('/tiktok/app/v3/video-comments', {
params: { aweme_id: '7123456789012345678', cursor: 0, count: 20 }
});
GET /api/tikhub/tiktok/analytics/video-metrics — 视频数据指标| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| video_id | string | ✅ | 视频 ID |
const { data } = await tikhubClient.get('/tiktok/analytics/video-metrics', {
params: { video_id: '7123456789012345678' }
});
POST /api/tikhub/tiktok/creator/account-insights — 创作者账户洞察const { data } = await tikhubClient.post('/tiktok/creator/account-insights', {
// 请求体参数根据 TikHub API 文档填写
start_date: '2025-01-01',
end_date: '2025-01-31'
});
GET /api/tikhub/tiktok/shop/product-detail — TikTok 商品详情| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| product_id | string | ✅ | 商品 ID |
const { data } = await tikhubClient.get('/tiktok/shop/product-detail', {
params: { product_id: '1234567890' }
});
GET /api/tikhub/tiktok/ads/ads-detail — 广告详情| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ad_id | string | ✅ | 广告 ID |
const { data } = await tikhubClient.get('/tiktok/ads/ads-detail', {
params: { ad_id: 'AD12345' }
});
| HTTP 状态码 | 说明 | 处理建议 |
|---|---|---|
| 400 | 请求参数错误 | 检查必填参数是否缺失、参数格式是否正确 |
| 401 | 认证失败 | Amazon 接口:检查 shop-objectId 是否正确;其他平台:联系后端检查 Token 配置 |
| 403 | 权限不足 | 检查店铺是否有对应 API 权限 |
| 404 | 资源不存在 | 检查请求路径和参数 ID 是否正确 |
| 429 | 请求频率超限 | 降低请求频率,建议添加请求节流/防抖 |
| 500 | 服务器内部错误 | 联系后端排查,可能是上游 API 异常 |
| 502 | 上游服务错误 | 第三方平台 API 返回异常,稍后重试 |
| 503 | 服务不可用 | 服务维护中,稍后重试 |