@gt6/sdk
Version:
GT6 SDK for articles management - A comprehensive JavaScript/TypeScript library for managing articles, categories, and tags in GT6 platform
1,326 lines (1,322 loc) • 152 kB
JavaScript
// 错误类型
class GT6Error extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.name = 'GT6Error';
}
}
class GT6Client {
constructor(config) {
this.config = {
timeout: 10000,
cache: {
enabled: true,
ttl: 300000 // 5分钟默认缓存
},
...config
};
this.cache = new Map();
}
/**
* 发起HTTP请求
*/
async request(endpoint, options = {}) {
const url = `${this.config.baseUrl}${endpoint}`;
const cacheKey = `${options.method || 'GET'}:${url}`;
// 检查缓存(只对GET请求缓存)
if (this.config.cache?.enabled && options.method !== 'POST' && options.method !== 'PUT' && options.method !== 'DELETE') {
const cached = this.cache.get(cacheKey);
if (cached && Date.now() - cached.timestamp < this.config.cache.ttl) {
return cached.data;
}
}
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), this.config.timeout);
// 对于FormData,不设置Content-Type,让浏览器自动设置
const headers = new Headers(options.headers);
if (!(options.body instanceof FormData)) {
headers.set('Content-Type', 'application/json');
}
const response = await fetch(url, {
...options,
headers,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
throw new GT6Error(`HTTP ${response.status}: ${response.statusText}`, response.status);
}
const data = await response.json();
// 更新缓存(只对GET请求缓存)
if (this.config.cache?.enabled && options.method !== 'POST' && options.method !== 'PUT' && options.method !== 'DELETE') {
this.cache.set(cacheKey, { data, timestamp: Date.now() });
}
return data;
}
catch (error) {
if (error instanceof GT6Error) {
throw error;
}
if (error.name === 'AbortError') {
throw new GT6Error('Request timeout', 408);
}
throw new GT6Error(`Network error: ${error.message}`);
}
}
/**
* 获取配置
*/
getConfig() {
return { ...this.config };
}
/**
* 清除缓存
*/
clearCache() {
this.cache.clear();
}
/**
* 获取缓存统计
*/
getCacheStats() {
const entries = Array.from(this.cache.entries()).map(([key, value]) => ({
key,
age: Date.now() - value.timestamp
}));
return {
size: this.cache.size,
entries
};
}
}
class ArticlesAPI {
constructor(client) {
this.client = client;
}
/**
* 1. 根据文章ID获取文章详情
*/
async getArticle(articleId) {
return this.client.request(`/articles/${articleId}.json`);
}
/**
* 1.1 根据文章ID获取文章详情(平台使用)
*/
async getArticle_p(articleId) {
return this.client.request(`/parentarticles/${articleId}.json`);
}
/**
* 2. 获取文章分类列表
*/
async getCategories(rootCategoryId) {
const config = this.client.getConfig();
const categoryId = rootCategoryId || config.rootCategoryId || '671920';
const response = await this.client.request(`/article-categories/platform-${config.platformId}-root-${categoryId}.json`);
return response.categories;
}
/**
* 2.1 获取文章分类列表(使用下级平台ID)
*/
async getCategories_p(rootCategoryId_p) {
const config = this.client.getConfig();
const categoryId = rootCategoryId_p || config.rootCategoryId_p || '671920';
const response = await this.client.request(`/article-categories/platform/platform-${config.platformId_p}-root-${categoryId}.json`);
return response.categories;
}
/**
* 2.2 获取文章分类列表(使用下级平台ID)
*/
async getCategories_a(rootCategoryId) {
const config = this.client.getConfig();
const categoryId = rootCategoryId || config.rootCategoryId;
const response = await this.client.request(`/big-article-categories/platform-${config.platformId}-root-${categoryId}.json`);
return response.categories;
}
/**
* 3. 获取文章标签列表
*/
async getTags(tagAlias) {
const config = this.client.getConfig();
const alias = tagAlias || config.tagAlias || '001';
const response = await this.client.request(`/article-tags/platform-${config.platformId}-${alias}.json`);
return response.tags;
}
/**
* 3.1 获取文章标签列表(使用下级平台ID)
*/
async getTags_p(tagAlias_p) {
const config = this.client.getConfig();
const alias = tagAlias_p || config.tagAlias_p || '001';
const response = await this.client.request(`/article-tags/platform-${config.platformId_p}-${alias}.json`);
return response.tags;
}
/**
* 3.2. 获取单个平台文章标签列表
*/
async getTags_p_single(tagId_p) {
const config = this.client.getConfig();
const tagId = tagId_p || config.tagId_p;
if (!tagId) {
throw new Error('Article tag ID is required');
}
const response = await this.client.request(`/article-tags/${tagId}.json`);
return response;
}
/**
* 3.3. 获取单个平台文章标签的文章ID列表
*/
async getTagArticleIds_p(tagId_p) {
const tagData = await this.getTags_p_single(tagId_p);
return tagData.articleIds;
}
/**
* 4. 根据分类ID获取文章列表
* 支持单个分类ID或分类ID数组
*/
async getArticlesByCategory(categoryId, options) {
// 获取分类数据
const categories = await this.getCategories();
// 将单个分类ID转换为数组
const categoryIds = Array.isArray(categoryId) ? categoryId : [categoryId];
// 收集所有指定分类下的文章ID
const allArticleIds = [];
categoryIds.forEach(id => {
const targetCategory = categories.find(cat => cat.categoryId === id);
if (targetCategory) {
allArticleIds.push(...targetCategory.articleIds);
}
});
// 去重
const uniqueArticleIds = [...new Set(allArticleIds)];
if (uniqueArticleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = uniqueArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: uniqueArticleIds.length,
page,
limit
};
}
/**
* 4.1 根据分类ID获取文章列表
* 支持单个分类ID或分类ID数组
*/
async getArticlesByCategory_p(categoryId, options) {
// 获取分类数据
const categories = await this.getCategories_p();
// 将单个分类ID转换为数组
const categoryIds = Array.isArray(categoryId) ? categoryId : [categoryId];
// 收集所有指定分类下的文章ID
const allArticleIds = [];
categoryIds.forEach(id => {
const targetCategory = categories.find(cat => cat.categoryId === id);
if (targetCategory) {
allArticleIds.push(...targetCategory.articleIds);
}
});
// 去重
const uniqueArticleIds = [...new Set(allArticleIds)];
if (uniqueArticleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = uniqueArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_p(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: uniqueArticleIds.length,
page,
limit
};
}
/**
* 4.2 根据分类ID获取文章列表
* 支持单个分类ID或分类ID数组
*/
async getArticlesByCategory_t(categoryId, options) {
// 获取分类数据
const categories = await this.getCategories();
// 将单个分类ID转换为数组
const categoryIds = Array.isArray(categoryId) ? categoryId : [categoryId];
// 收集所有指定分类下的文章ID
const allArticleIds = [];
categoryIds.forEach(id => {
const targetCategory = categories.find(cat => cat.categoryId === id);
if (targetCategory) {
allArticleIds.push(...targetCategory.articleIds);
}
});
// 去重
const uniqueArticleIds = [...new Set(allArticleIds)];
if (uniqueArticleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = uniqueArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_p(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: uniqueArticleIds.length,
page,
limit
};
}
/**
* 4.3 根据分类ID获取文章列表(使用大型分类静态文件)
* 只支持单个分类ID,从大型分类静态文件获取文章ID列表
*/
async getArticlesByCategory_a(categoryId, options) {
// 从大型分类静态文件获取该分类的文章ID列表
const categoryData = await this.client.request(`/big-article-categories/${categoryId}.json`);
if (!categoryData || !categoryData.articleIds || categoryData.articleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = categoryData.articleIds.slice(offset, offset + limit);
// 获取文章详情(使用上级平台数据)
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_p(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: categoryData.articleIds.length,
page,
limit
};
}
/**
* 5. 根据标签ID获取文章列表
* 支持单个标签ID或标签ID数组
*/
async getArticlesByTag(tagId, options) {
// 获取标签数据,传递tagAlias参数
const tags = await this.getTags(options?.tagAlias);
// 将单个标签ID转换为数组
const tagIds = Array.isArray(tagId) ? tagId : [tagId];
// 收集所有指定标签下的文章ID
const allArticleIds = [];
tagIds.forEach(id => {
const targetTag = tags.find(tag => tag.tagId === id);
if (targetTag) {
allArticleIds.push(...targetTag.articleIds);
}
});
// 去重
const uniqueArticleIds = [...new Set(allArticleIds)];
if (uniqueArticleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = uniqueArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: uniqueArticleIds.length,
page,
limit
};
}
/**
* 5.1 根据标签ID获取文章列表
* 支持单个标签ID或标签ID数组
*/
async getArticlesByTag_p(tagId, options) {
// 获取标签数据,传递tagAlias参数
const tags = await this.getTags_p(options?.tagAlias);
// 将单个标签ID转换为数组
const tagIds = Array.isArray(tagId) ? tagId : [tagId];
// 收集所有指定标签下的文章ID
const allArticleIds = [];
tagIds.forEach(id => {
const targetTag = tags.find(tag => tag.tagId === id);
if (targetTag) {
allArticleIds.push(...targetTag.articleIds);
}
});
// 去重
const uniqueArticleIds = [...new Set(allArticleIds)];
if (uniqueArticleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = uniqueArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_p(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: uniqueArticleIds.length,
page,
limit
};
}
/**
* 5.2 根据标签ID获取文章列表
* 支持单个标签ID或标签ID数组
*/
async getArticlesByTag_t(tagId, options) {
// 获取标签数据,传递tagAlias参数
const tags = await this.getTags(options?.tagAlias);
// 将单个标签ID转换为数组
const tagIds = Array.isArray(tagId) ? tagId : [tagId];
// 收集所有指定标签下的文章ID
const allArticleIds = [];
tagIds.forEach(id => {
const targetTag = tags.find(tag => tag.tagId === id);
if (targetTag) {
allArticleIds.push(...targetTag.articleIds);
}
});
// 去重
const uniqueArticleIds = [...new Set(allArticleIds)];
if (uniqueArticleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = uniqueArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_p(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: uniqueArticleIds.length,
page,
limit
};
}
/**
* 5.3. 根据单个标签ID获取平台文章列表
* 使用getTags_p_single获取指定标签数据,然后获取文章详情
* @param tagId 单个标签ID
* @param options 查询选项
*/
async getArticlesByTag_single(tagId, options) {
// 获取指定标签的数据
const tagData = await this.getTags_p_single(tagId);
if (!tagData || !tagData.articleIds || tagData.articleIds.length === 0) {
// 剔除articleIds字段
const { articleIds, ...tagInfo } = tagData;
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10,
tag: tagInfo
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = tagData.articleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_p(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
// 剔除articleIds字段
const { articleIds, ...tagInfo } = tagData;
return {
articles: filteredArticles,
total: tagData.articleIds.length,
page,
limit,
tag: tagInfo
};
}
/**
* 6. 根据分类ID获取该分类的层级路径
* 用于前端面包屑导航
*/
async getCategoryPath(categoryId) {
// 获取所有分类数据
const categories = await this.getCategories();
// 查找目标分类
const targetCategory = categories.find(cat => cat.categoryId === categoryId);
if (!targetCategory) {
return {
path: [],
currentCategory: null,
breadcrumbs: []
};
}
// 构建分类路径
const path = [];
const breadcrumbs = [];
// 递归查找父分类
const buildPath = (currentCategory, level = 0) => {
// 将当前分类添加到路径开头(因为我们要从根到叶子构建)
path.unshift(currentCategory);
breadcrumbs.unshift({
categoryId: currentCategory.categoryId,
categoryName: currentCategory.categoryName,
level
});
// 如果有父分类,继续递归
if (currentCategory.parentId && currentCategory.parentId !== 0) {
const parentCategory = categories.find(cat => cat.categoryId === currentCategory.parentId);
if (parentCategory) {
buildPath(parentCategory, level + 1);
}
}
};
// 从目标分类开始构建路径
buildPath(targetCategory);
return {
path,
currentCategory: targetCategory,
breadcrumbs
};
}
/**
* 7. 获取指定分类ID下的子分类
* 支持递归获取所有层级的子分类
*/
async getSubCategories(categoryId, options) {
// 获取所有分类数据
const categories = await this.getCategories();
// 查找目标分类
const targetCategory = categories.find(cat => cat.categoryId === categoryId);
if (!targetCategory) {
return {
subCategories: [],
currentCategory: null,
total: 0,
depth: 0
};
}
const subCategories = [];
let maxDepth = options?.maxDepth || Infinity;
let actualDepth = 0;
// 递归获取子分类的函数
const collectSubCategories = (parentId, currentDepth = 0) => {
if (currentDepth >= maxDepth) {
return;
}
// 查找直接子分类
const children = categories.filter(cat => cat.parentId === parentId);
children.forEach(child => {
subCategories.push(child);
// 如果需要递归且未达到最大深度,继续查找子分类
if (options?.recursive && currentDepth < maxDepth - 1) {
collectSubCategories(child.categoryId, currentDepth + 1);
}
});
// 更新实际深度
actualDepth = Math.max(actualDepth, currentDepth);
};
// 开始收集子分类
collectSubCategories(categoryId);
// 如果需要包含当前分类,添加到结果中
if (options?.includeCurrent) {
subCategories.unshift(targetCategory);
}
return {
subCategories,
currentCategory: targetCategory,
total: subCategories.length,
depth: actualDepth
};
}
/******************************************************************************************
* 1.1 根据文章ID获取文章详情(平台用户都可以使用)
*/
async getArticle_all(articleId) {
return this.client.request(`/articles_all/${articleId}.json`);
}
/**
* 1.2 根据分类ID获取文章列表(平台用户都可以使用)
*/
async getArticlesByCategory_all(categoryId, options) {
// 从大型分类静态文件获取该分类的文章ID列表
const categoryData = await this.client.request(`/big-article-categories/${categoryId}.json`);
if (!categoryData || !categoryData.articleIds || categoryData.articleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 将文章ID列表倒序排列
const reversedArticleIds = [...categoryData.articleIds].reverse();
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = reversedArticleIds.slice(offset, offset + limit);
// 获取文章详情 - 修复:提取 articleId 字段
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_all(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: categoryData.articleIds.length,
page,
limit
};
}
/**
* 1.2.2 根据分类ID获取文章列表(支持搜索过滤)
*/
async getArticlesByCategory_all_search(categoryId, searchOptions) {
// 从大型分类静态文件获取该分类的文章ID列表
const categoryData = await this.client.request(`/big-article-categories/${categoryId}.json`);
if (!categoryData || !categoryData.articleIds || categoryData.articleIds.length === 0) {
return {
articles: [],
total: 0,
page: searchOptions?.page || 1,
limit: searchOptions?.limit || 10,
filteredTotal: 0
};
}
// 将文章数据倒序排列
const reversedArticleIds = [...categoryData.articleIds].reverse();
// 应用搜索过滤
let filteredArticleIds = reversedArticleIds.filter(articleObj => {
// 1. regionId 过滤
if (searchOptions?.regionIds && searchOptions.regionIds.length > 0) {
if (!articleObj.regionId || !searchOptions.regionIds.includes(articleObj.regionId)) {
return false;
}
}
// 2. price 范围过滤
if (searchOptions?.priceRange) {
const price = parseFloat(articleObj.price || '0');
if (searchOptions.priceRange.min !== undefined && price < searchOptions.priceRange.min) {
return false;
}
if (searchOptions.priceRange.max !== undefined && price > searchOptions.priceRange.max) {
return false;
}
}
// 3. Salary 范围过滤
if (searchOptions?.salaryRange) {
const salary = parseFloat(articleObj.Salary || '0');
if (searchOptions.salaryRange.min !== undefined && salary < searchOptions.salaryRange.min) {
return false;
}
if (searchOptions.salaryRange.max !== undefined && salary > searchOptions.salaryRange.max) {
return false;
}
}
// 4. mileage 范围过滤
if (searchOptions?.mileageRange) {
// 处理里程数格式,如 "26000 km" -> 26000
const mileageStr = articleObj.Mileage || '0';
const mileageMatch = mileageStr.match(/(\d+(?:\.\d+)?)/);
const mileage = mileageMatch ? parseFloat(mileageMatch[1]) : 0;
if (searchOptions.mileageRange.min !== undefined && mileage < searchOptions.mileageRange.min) {
return false;
}
if (searchOptions.mileageRange.max !== undefined && mileage > searchOptions.mileageRange.max) {
return false;
}
}
// 5. propertyArea 范围过滤
if (searchOptions?.propertyAreaRange) {
const propertyArea = parseFloat(articleObj.propertyArea || '0');
if (searchOptions.propertyAreaRange.min !== undefined && propertyArea < searchOptions.propertyAreaRange.min) {
return false;
}
if (searchOptions.propertyAreaRange.max !== undefined && propertyArea > searchOptions.propertyAreaRange.max) {
return false;
}
}
// 6. plotArea 范围过滤
if (searchOptions?.plotAreaRange) {
const plotArea = parseFloat(articleObj.plotArea || '0');
if (searchOptions.plotAreaRange.min !== undefined && plotArea < searchOptions.plotAreaRange.min) {
return false;
}
if (searchOptions.plotAreaRange.max !== undefined && plotArea > searchOptions.plotAreaRange.max) {
return false;
}
}
// 7. tagIds 过滤
if (searchOptions?.tagIds && searchOptions.tagIds.length > 0) {
if (!articleObj.tagIds || articleObj.tagIds.length === 0) {
return false;
}
if (searchOptions.requireAllTags) {
// 要求包含所有指定的 tagIds
const hasAllTags = searchOptions.tagIds.every(tagId => articleObj.tagIds.includes(tagId));
if (!hasAllTags) {
return false;
}
}
else {
// 包含任意一个指定的 tagId 即可
const hasAnyTag = searchOptions.tagIds.some(tagId => articleObj.tagIds.includes(tagId));
if (!hasAnyTag) {
return false;
}
}
}
return true;
});
const filteredTotal = filteredArticleIds.length;
if (filteredTotal === 0) {
return {
articles: [],
total: categoryData.articleIds.length,
page: searchOptions?.page || 1,
limit: searchOptions?.limit || 10,
filteredTotal: 0
};
}
// 应用分页
const page = searchOptions?.page || 1;
const limit = searchOptions?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = filteredArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleObj => this.getArticle_all(articleObj.articleId)));
// 应用状态过滤
let finalArticles = articles;
if (searchOptions?.status) {
finalArticles = articles.filter(article => article.status === searchOptions.status);
}
return {
articles: finalArticles,
total: categoryData.articleIds.length, // 原始总数
page,
limit,
filteredTotal // 过滤后的总数
};
}
/**
* 1.2.2 根据分类ID获取文章列表(支持搜索过滤)
*/
async getArticlesByCategory_all_search01(categoryId, searchOptions) {
// 将单个分类ID转换为数组
const categoryIds = Array.isArray(categoryId) ? categoryId : [categoryId];
// 收集所有指定分类下的文章数据
const allArticleData = [];
let totalArticles = 0;
// 遍历所有分类ID,获取每个分类的文章数据
for (const singleCategoryId of categoryIds) {
try {
const categoryData = await this.client.request(`/big-article-categories/${singleCategoryId}.json`);
if (categoryData && categoryData.articleIds && categoryData.articleIds.length > 0) {
// 将每个分类的文章数据倒序排列后再追加
const reversedArticleIds = [...categoryData.articleIds].reverse();
allArticleData.push(...reversedArticleIds);
totalArticles += categoryData.articleIds.length;
}
}
catch (error) {
console.warn(`Failed to fetch data for category ${singleCategoryId}:`, error);
// 继续处理其他分类,不中断整个流程
}
}
if (allArticleData.length === 0) {
return {
articles: [],
total: 0,
page: searchOptions?.page || 1,
limit: searchOptions?.limit || 10,
filteredTotal: 0
};
}
// 应用搜索过滤
let filteredArticleIds = allArticleData.filter(articleObj => {
// 1. regionId 过滤
if (searchOptions?.regionIds && searchOptions.regionIds.length > 0) {
if (!articleObj.regionId || !searchOptions.regionIds.includes(articleObj.regionId)) {
return false;
}
}
// 2. price 范围过滤
if (searchOptions?.priceRange) {
const price = parseFloat(articleObj.price || '0');
if (searchOptions.priceRange.min !== undefined && price < searchOptions.priceRange.min) {
return false;
}
if (searchOptions.priceRange.max !== undefined && price > searchOptions.priceRange.max) {
return false;
}
}
// 3. Salary 范围过滤
if (searchOptions?.salaryRange) {
const salary = parseFloat(articleObj.Salary || '0');
if (searchOptions.salaryRange.min !== undefined && salary < searchOptions.salaryRange.min) {
return false;
}
if (searchOptions.salaryRange.max !== undefined && salary > searchOptions.salaryRange.max) {
return false;
}
}
// 4. mileage 范围过滤
if (searchOptions?.mileageRange) {
// 处理里程数格式,如 "26000 km" -> 26000
const mileageStr = articleObj.Mileage || '0';
const mileageMatch = mileageStr.match(/(\d+(?:\.\d+)?)/);
const mileage = mileageMatch ? parseFloat(mileageMatch[1]) : 0;
if (searchOptions.mileageRange.min !== undefined && mileage < searchOptions.mileageRange.min) {
return false;
}
if (searchOptions.mileageRange.max !== undefined && mileage > searchOptions.mileageRange.max) {
return false;
}
}
// 5. propertyArea 范围过滤
if (searchOptions?.propertyAreaRange) {
const propertyArea = parseFloat(articleObj.propertyArea || '0');
if (searchOptions.propertyAreaRange.min !== undefined && propertyArea < searchOptions.propertyAreaRange.min) {
return false;
}
if (searchOptions.propertyAreaRange.max !== undefined && propertyArea > searchOptions.propertyAreaRange.max) {
return false;
}
}
// 6. plotArea 范围过滤
if (searchOptions?.plotAreaRange) {
const plotArea = parseFloat(articleObj.plotArea || '0');
if (searchOptions.plotAreaRange.min !== undefined && plotArea < searchOptions.plotAreaRange.min) {
return false;
}
if (searchOptions.plotAreaRange.max !== undefined && plotArea > searchOptions.plotAreaRange.max) {
return false;
}
}
// 7. tagIds 过滤
if (searchOptions?.tagIds && searchOptions.tagIds.length > 0) {
if (!articleObj.tagIds || articleObj.tagIds.length === 0) {
return false;
}
if (searchOptions.requireAllTags) {
// 要求包含所有指定的 tagIds
const hasAllTags = searchOptions.tagIds.every(tagId => articleObj.tagIds.includes(tagId));
if (!hasAllTags) {
return false;
}
}
else {
// 包含任意一个指定的 tagId 即可
const hasAnyTag = searchOptions.tagIds.some(tagId => articleObj.tagIds.includes(tagId));
if (!hasAnyTag) {
return false;
}
}
}
return true;
});
const filteredTotal = filteredArticleIds.length;
if (filteredTotal === 0) {
return {
articles: [],
total: totalArticles,
page: searchOptions?.page || 1,
limit: searchOptions?.limit || 10,
filteredTotal: 0
};
}
// 应用分页
const page = searchOptions?.page || 1;
const limit = searchOptions?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = filteredArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleObj => this.getArticle_all(articleObj.articleId)));
// 应用状态过滤
let finalArticles = articles;
if (searchOptions?.status) {
finalArticles = articles.filter(article => article.status === searchOptions.status);
}
return {
articles: finalArticles,
total: totalArticles, // 原始总数
page,
limit,
filteredTotal // 过滤后的总数
};
}
/**
* 1.2.1 根据平台ID获取文章列表(平台用户都可以使用)
*/
async getArticlesByplatform_all(platformId, options) {
// 从大型分类静态文件获取该分类的文章ID列表
const categoryData = await this.client.request(`/platform-advertise/platform-${platformId}.json`);
if (!categoryData || !categoryData.articleIds || categoryData.articleIds.length === 0) {
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = categoryData.articleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_all(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
return {
articles: filteredArticles,
total: categoryData.articleIds.length,
page,
limit
};
}
/**
* 1.3 获取文章分类列表(平台用户都可以使用)
*/
async getArticlesCategories_all(rootCategoryId) {
const config = this.client.getConfig();
const categoryId = rootCategoryId || config.rootCategoryId;
const response = await this.client.request(`/big-article-categories/platform-${config.platformId}-root-${categoryId}.json`);
return response.categories;
}
/**
* 1.4 根据单个标签ID获取平台文章列表
* 使用getTags_p_single获取指定标签数据,然后获取文章详情
* @param tagId 单个标签ID
* @param options 查询选项
*/
async getArticlesByTag_single_all(tagId, options) {
// 获取指定标签的数据
const tagData = await this.getTags_p_single(tagId);
if (!tagData || !tagData.articleIds || tagData.articleIds.length === 0) {
// 剔除articleIds字段
const { articleIds, ...tagInfo } = tagData;
return {
articles: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10,
tag: tagInfo
};
}
// 将文章ID列表倒序排列
const reversedArticleIds = [...tagData.articleIds].reverse();
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedArticleIds = reversedArticleIds.slice(offset, offset + limit);
// 获取文章详情
const articles = await Promise.all(paginatedArticleIds.map(articleId => this.getArticle_all(articleId)));
// 应用状态过滤
let filteredArticles = articles;
if (options?.status) {
filteredArticles = articles.filter(article => article.status === options.status);
}
// 剔除articleIds字段
const { articleIds, ...tagInfo } = tagData;
return {
articles: filteredArticles,
total: tagData.articleIds.length,
page,
limit,
tag: tagInfo
};
}
}
class ProductsAPI {
constructor(client) {
this.client = client;
}
/**
* 1. 根据产品ID获取产品详情
*/
async getProduct(productId) {
const product = await this.client.request(`/products/${productId}.json`);
// 检查产品是否有销售区域和税费模板
if (product.regions && product.regions.length > 0 &&
product.taxTemplates && product.taxTemplates.length > 0) {
try {
// 获取税费信息
const taxInfo = await this.getTaxInfo();
// 为每个税费模板添加规则
product.taxTemplates = product.taxTemplates.map(template => {
const matchingTemplate = taxInfo.templates.find(t => t.templateId === template.templateId);
if (matchingTemplate) {
// 过滤出产品可销售区域的规则
const productRegionIds = product.regions.map(r => r.regionId);
const applicableRules = matchingTemplate.rules.filter(rule => productRegionIds.includes(rule.regionId));
return {
...template,
rules: applicableRules
};
}
return template;
});
}
catch (error) {
// 如果获取税费信息失败,不影响产品详情返回
console.warn('Failed to fetch tax info:', error);
}
}
// 检查产品是否有销售区域和运费模板
if (product.regions && product.regions.length > 0 &&
product.shippingTemplates && product.shippingTemplates.length > 0) {
try {
// 获取运费信息
const shippingInfo = await this.getShippingInfo();
// 为每个运费模板添加规则
product.shippingTemplates = product.shippingTemplates.map(template => {
const matchingTemplate = shippingInfo.templates.find(t => t.templateId === template.templateId);
if (matchingTemplate) {
// 过滤出产品可销售区域的规则
const productRegionIds = product.regions.map(r => r.regionId);
const applicableRules = matchingTemplate.rules.filter(rule => productRegionIds.includes(rule.regionId));
return {
...template,
rules: applicableRules
};
}
return template;
});
}
catch (error) {
// 如果获取运费信息失败,不影响产品详情返回
console.warn('Failed to fetch shipping info:', error);
}
}
return product;
}
/**
* 1.1. 根据产品ID获取父平台产品详情
*/
async getProduct_p(productId) {
const product = await this.client.request(`/parentproducts/${productId}.json`);
// 检查产品是否有销售区域和税费模板
if (product.regions && product.regions.length > 0 &&
product.taxTemplates && product.taxTemplates.length > 0) {
try {
// 获取税费信息
const taxInfo = await this.getTaxInfo();
// 为每个税费模板添加规则
product.taxTemplates = product.taxTemplates.map(template => {
const matchingTemplate = taxInfo.templates.find(t => t.templateId === template.templateId);
if (matchingTemplate) {
// 过滤出产品可销售区域的规则
const productRegionIds = product.regions.map(r => r.regionId);
const applicableRules = matchingTemplate.rules.filter(rule => productRegionIds.includes(rule.regionId));
return {
...template,
rules: applicableRules
};
}
return template;
});
}
catch (error) {
// 如果获取税费信息失败,不影响产品详情返回
console.warn('Failed to fetch tax info:', error);
}
}
// 检查产品是否有销售区域和运费模板
if (product.regions && product.regions.length > 0 &&
product.shippingTemplates && product.shippingTemplates.length > 0) {
try {
// 获取运费信息
const shippingInfo = await this.getShippingInfo();
// 为每个运费模板添加规则
product.shippingTemplates = product.shippingTemplates.map(template => {
const matchingTemplate = shippingInfo.templates.find(t => t.templateId === template.templateId);
if (matchingTemplate) {
// 过滤出产品可销售区域的规则
const productRegionIds = product.regions.map(r => r.regionId);
const applicableRules = matchingTemplate.rules.filter(rule => productRegionIds.includes(rule.regionId));
return {
...template,
rules: applicableRules
};
}
return template;
});
}
catch (error) {
// 如果获取运费信息失败,不影响产品详情返回
console.warn('Failed to fetch shipping info:', error);
}
}
return product;
}
/**
* 2. 获取产品分类列表
*/
async getCategories(productRootCategoryId) {
const config = this.client.getConfig();
const categoryId = productRootCategoryId || config.productRootCategoryId || '277233';
const response = await this.client.request(`/product-categories/platform-${config.platformId}-root-${categoryId}.json`);
return response.categories;
}
/**
* 2.1. 获取平台产品分类列表
*/
async getCategories_p(productRootCategoryId_p) {
const config = this.client.getConfig();
const categoryId = productRootCategoryId_p || config.productRootCategoryId_p;
const response = await this.client.request(`/product-categories/platform/platform-${config.platformId_p}-root-${categoryId}.json`);
return response.categories;
}
/**
* 2.2. 获取平台产品分类列表
*/
async getCategories_b(productRootCategoryId) {
const config = this.client.getConfig();
const categoryId = productRootCategoryId || config.productRootCategoryId;
const response = await this.client.request(`/big-product-categories/platform-${config.platformId}-root-${categoryId}.json`);
return response.categories;
}
/**
* 3. 获取产品标签列表
*/
async getTags(productTagAlias) {
const config = this.client.getConfig();
const alias = productTagAlias || config.productTagAlias || '01';
const response = await this.client.request(`/product-tags/platform-${config.platformId}-${alias}.json`);
return response.tags;
}
/**
* 3.1. 获取平台产品标签列表
*/
async getTags_p(productTagAlias_p) {
const config = this.client.getConfig();
const alias = productTagAlias_p || config.productTagAlias_p;
const response = await this.client.request(`/product-tags/platform-${config.platformId_p}-${alias}.json`);
return response.tags;
}
/**
* 3.2. 获取单个平台产品标签列表
*/
async getTags_p_single(productTagId_p) {
const config = this.client.getConfig();
const productTagId = productTagId_p || config.productTagId_p;
if (!productTagId) {
throw new Error('Product tag ID is required');
}
const response = await this.client.request(`/product-tags/${productTagId}.json`);
return response;
}
/**
* 3.3. 获取单个平台产品标签的产品ID列表
*/
async getTagProductIds_p(productTagId_p) {
const tagData = await this.getTags_p_single(productTagId_p);
return tagData.productIds;
}
/**
* 4. 根据分类ID获取产品列表
* 支持单个分类ID或分类ID数组
*/
async getProductsByCategory(categoryId, options) {
// 获取分类数据
const categories = await this.getCategories();
// 递归查找分类的函数
const findCategoryById = (cats, targetId) => {
for (const cat of cats) {
if (cat.categoryId === targetId) {
return cat;
}
if (cat.children && cat.children.length > 0) {
const found = findCategoryById(cat.children, targetId);
if (found)
return found;
}
}
return null;
};
// 将单个分类ID转换为数组
const categoryIds = Array.isArray(categoryId) ? categoryId : [categoryId];
// 收集所有指定分类下的产品ID
const allProductIds = [];
categoryIds.forEach(id => {
const targetCategory = findCategoryById(categories, id);
if (targetCategory) {
allProductIds.push(...targetCategory.productIds);
}
});
// 去重
const uniqueProductIds = [...new Set(allProductIds)];
if (uniqueProductIds.length === 0) {
return {
products: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedProductIds = uniqueProductIds.slice(offset, offset + limit);
// 获取产品详情
const products = await Promise.all(paginatedProductIds.map(productId => this.getProduct(productId)));
// 应用状态过滤
let filteredProducts = products;
if (options?.status !== undefined) {
filteredProducts = products.filter(product => product.status === options.status);
}
return {
products: filteredProducts,
total: uniqueProductIds.length,
page,
limit
};
}
/**
* 4.1. 根据分类ID获取平台产品列表
*/
async getProductsByCategory_p(categoryId, options) {
// 获取分类数据
const categories = await this.getCategories_p();
// 递归查找分类的函数
const findCategoryById = (cats, targetId) => {
for (const cat of cats) {
if (cat.categoryId === targetId) {
return cat;
}
if (cat.children && cat.children.length > 0) {
const found = findCategoryById(cat.children, targetId);
if (found)
return found;
}
}
return null;
};
// 将单个分类ID转换为数组
const categoryIds = Array.isArray(categoryId) ? categoryId : [categoryId];
// 收集所有指定分类下的产品ID
const allProductIds = [];
categoryIds.forEach(id => {
const targetCategory = findCategoryById(categories, id);
if (targetCategory) {
allProductIds.push(...targetCategory.productIds);
}
});
// 去重
const uniqueProductIds = [...new Set(allProductIds)];
if (uniqueProductIds.length === 0) {
return {
products: [],
total: 0,
page: options?.page || 1,
limit: options?.limit || 10
};
}
// 应用分页
const page = options?.page || 1;
const limit = options?.limit || 10;
const offset = (page - 1) * limit;
const paginatedProductIds = uniqueProductIds.slice(offset, offset + limit);
// 获取产品详情
const products = await Promise.all(paginatedProductIds.map(productId => this.getProduct_p(productId)));
// 应用状态过滤
let filteredProducts = products;
if (options?.status !== undefined) {
filteredProducts = products.filter(product => product.status === options.status);
}