185 lines
5.5 KiB
TypeScript
185 lines
5.5 KiB
TypeScript
import { ILogger, Inject, Provide } from '@midwayjs/core';
|
||
import { ShopyyAdapter } from '../adapter/shopyy.adapter';
|
||
import { WooCommerceAdapter } from '../adapter/woocommerce.adapter';
|
||
import { ISiteAdapter } from '../interface/site-adapter.interface';
|
||
import { ShopyyService } from './shopyy.service';
|
||
import { SiteService } from './site.service';
|
||
import { WPService } from './wp.service';
|
||
import { ProductService } from './product.service';
|
||
import { UnifiedProductDTO } from '../dto/site-api.dto';
|
||
import { Product } from '../entity/product.entity';
|
||
|
||
@Provide()
|
||
export class SiteApiService {
|
||
@Inject()
|
||
siteService: SiteService;
|
||
|
||
@Inject()
|
||
wpService: WPService;
|
||
|
||
@Inject()
|
||
shopyyService: ShopyyService;
|
||
|
||
@Inject()
|
||
productService: ProductService;
|
||
|
||
@Inject()
|
||
logger: ILogger;
|
||
|
||
async getAdapter(siteId: number): Promise<ISiteAdapter> {
|
||
const site = await this.siteService.get(siteId, true);
|
||
if (!site) {
|
||
throw new Error(`Site ${siteId} not found`);
|
||
}
|
||
|
||
if (site.type === 'woocommerce') {
|
||
if (!site?.consumerKey || !site.consumerSecret || !site.apiUrl) {
|
||
throw new Error('站点配置缺少 consumerKey/consumerSecret/apiUrl');
|
||
}
|
||
return new WooCommerceAdapter(site, this.wpService);
|
||
} else if (site.type === 'shopyy') {
|
||
if (!site?.token) {
|
||
throw new Error('站点配置缺少 token');
|
||
}
|
||
return new ShopyyAdapter(site, this.shopyyService);
|
||
}
|
||
|
||
throw new Error(`Unsupported site type: ${site.type}`);
|
||
}
|
||
|
||
/**
|
||
* 获取站点商品并关联ERP产品信息
|
||
* @param siteId 站点ID
|
||
* @param siteProduct 站点商品信息
|
||
* @returns 包含ERP产品信息的站点商品
|
||
*/
|
||
async enrichSiteProductWithErpInfo(siteId: number, siteProduct: UnifiedProductDTO): Promise<UnifiedProductDTO & { erpProduct?: Product }> {
|
||
if (!siteProduct || !siteProduct.sku) {
|
||
return siteProduct;
|
||
}
|
||
|
||
try {
|
||
// 使用站点SKU查询对应的ERP产品
|
||
const erpProduct = await this.productService.findProductBySiteSku(siteProduct.sku);
|
||
|
||
// 将ERP产品信息合并到站点商品中
|
||
return {
|
||
...siteProduct,
|
||
erpProduct,
|
||
};
|
||
} catch (error) {
|
||
// 如果找不到对应的ERP产品,返回原始站点商品
|
||
console.warn(`未找到站点SKU ${siteProduct.sku} 对应的ERP产品: ${error.message}`);
|
||
return siteProduct;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 批量获取站点商品并关联ERP产品信息
|
||
* @param siteId 站点ID
|
||
* @param siteProducts 站点商品列表
|
||
* @returns 包含ERP产品信息的站点商品列表
|
||
*/
|
||
async enrichSiteProductsWithErpInfo(siteId: number, siteProducts: UnifiedProductDTO[]): Promise<(UnifiedProductDTO & { erpProduct?: Product })[]> {
|
||
if (!siteProducts || !siteProducts.length) {
|
||
return siteProducts;
|
||
}
|
||
|
||
// 并行处理所有商品
|
||
const enrichedProducts = await Promise.all(
|
||
siteProducts.map(product => this.enrichSiteProductWithErpInfo(siteId, product))
|
||
);
|
||
|
||
return enrichedProducts;
|
||
}
|
||
|
||
/**
|
||
* 更新或创建产品
|
||
* @param siteId 站点ID
|
||
* @param product 产品数据
|
||
* @returns 更新或创建后的产品
|
||
*/
|
||
async upsertProduct(siteId: number, product: Partial<UnifiedProductDTO>): Promise<any> {
|
||
const adapter = await this.getAdapter(siteId);
|
||
|
||
// 首先尝试查找产品
|
||
if (!product.sku) {
|
||
throw new Error('产品SKU不能为空');
|
||
}
|
||
// 尝试搜索具有相同SKU的产品
|
||
let existingProduct
|
||
try {
|
||
|
||
existingProduct = await adapter.getProduct({ sku: product.sku });
|
||
} catch (error) {
|
||
this.logger.error(`[Site API] 查找产品失败, siteId: ${siteId}, sku: ${product.sku}, 错误信息: ${error.message}`);
|
||
existingProduct = null
|
||
}
|
||
if (existingProduct) {
|
||
// 找到现有产品,更新它
|
||
return await adapter.updateProduct({ id: existingProduct.id }, product);
|
||
}
|
||
// 产品不存在,执行创建
|
||
return await adapter.createProduct(product);
|
||
|
||
}
|
||
|
||
/**
|
||
* 批量更新或创建产品
|
||
* @param siteId 站点ID
|
||
* @param products 产品数据数组
|
||
* @returns 批量操作结果
|
||
*/
|
||
async batchUpsertProduct(siteId: number, products: Partial<UnifiedProductDTO>[]): Promise<{ created: any[], updated: any[], errors: any[] }> {
|
||
const results = {
|
||
created: [],
|
||
updated: [],
|
||
errors: []
|
||
};
|
||
|
||
for (const product of products) {
|
||
try {
|
||
const result = await this.upsertProduct(siteId, product);
|
||
// 判断是创建还是更新
|
||
if (result && result.id) {
|
||
// 简单判断:如果产品原本没有ID而现在有了,说明是创建的
|
||
if (!product.id || !product.id.toString().trim()) {
|
||
results.created.push(result);
|
||
} else {
|
||
results.updated.push(result);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
results.errors.push({
|
||
product: product,
|
||
error: error.message
|
||
});
|
||
}
|
||
}
|
||
|
||
return results;
|
||
}
|
||
|
||
/**
|
||
* 从站点获取产品
|
||
* @param siteId 站点ID
|
||
* @param params 查询参数
|
||
* @returns 站点产品列表
|
||
*/
|
||
async getProductsFromSite(siteId: number, params?: any): Promise<any> {
|
||
const adapter = await this.getAdapter(siteId);
|
||
return await adapter.getProducts(params);
|
||
}
|
||
|
||
/**
|
||
* 从站点获取所有产品
|
||
* @param siteId 站点ID
|
||
* @param params 查询参数
|
||
* @returns 站点产品列表
|
||
*/
|
||
async getAllProductsFromSite(siteId: number, params?: any): Promise<any[]> {
|
||
const adapter = await this.getAdapter(siteId);
|
||
return await adapter.getAllProducts(params);
|
||
}
|
||
}
|