forked from yoone/API
1
0
Fork 0
API/src/adapter/shopyy.adapter.ts

903 lines
31 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ISiteAdapter } from '../interface/site-adapter.interface';
import { ShopyyService } from '../service/shopyy.service';
import {
UnifiedCustomerDTO,
UnifiedMediaDTO,
UnifiedOrderDTO,
UnifiedOrderLineItemDTO,
UnifiedProductDTO,
UnifiedProductVariationDTO,
UnifiedSubscriptionDTO,
UnifiedReviewPaginationDTO,
UnifiedReviewDTO,
UnifiedWebhookDTO,
UnifiedWebhookPaginationDTO,
CreateWebhookDTO,
UpdateWebhookDTO,
UnifiedAddressDTO,
UnifiedShippingLineDTO,
OrderFulfillmentStatus,
FulfillmentDTO
} from '../dto/site-api.dto';
import { UnifiedPaginationDTO, UnifiedSearchParamsDTO, } from '../dto/api.dto';
import {
ShopyyCustomer,
ShopyyOrder,
ShopyyProduct,
ShopyyVariant,
ShopyyWebhook,
} from '../dto/shopyy.dto';
import {
OrderStatus,
} from '../enums/base.enum';
export class ShopyyAdapter implements ISiteAdapter {
shopyyFinancialStatusMap= {
'200': '待支付',
'210': "支付中",
'220':"部分支付",
'230':"已支付",
'240':"支付失败",
'250':"部分退款",
'260':"已退款",
'290':"已取消",
}
constructor(private site: any, private shopyyService: ShopyyService) {
this.mapCustomer = this.mapCustomer.bind(this);
this.mapProduct = this.mapProduct.bind(this);
this.mapVariation = this.mapVariation.bind(this);
this.mapOrder = this.mapOrder.bind(this);
this.mapMedia = this.mapMedia.bind(this);
// this.mapSubscription = this.mapSubscription.bind(this);
}
private mapMedia(item: any): UnifiedMediaDTO {
// 映射媒体项目
return {
id: item.id,
date_created: item.created_at,
date_modified: item.updated_at,
source_url: item.src,
title: item.alt || '',
media_type: '', // Shopyy API未提供,暂时留空
mime_type: '', // Shopyy API未提供,暂时留空
};
}
private mapMediaSearchParams(params: UnifiedSearchParamsDTO): any {
const { search, page, per_page } = params;
const shopyyParams: any = {
page: page || 1,
limit: per_page || 10,
};
if (search) {
shopyyParams.query = search;
}
return shopyyParams;
}
private mapProduct(item: ShopyyProduct & { permalink?: string }): UnifiedProductDTO {
// 映射产品状态
function mapProductStatus(status: number) {
return status === 1 ? 'publish' : 'draft';
}
return {
id: item.id,
name: item.name || item.title,
type: String(item.product_type ?? ''),
status: mapProductStatus(item.status),
sku: item.variant?.sku || '',
regular_price: String(item.variant?.price ?? ''),
sale_price: String(item.special_price ?? ''),
price: String(item.price ?? ''),
stock_status: item.inventory_tracking === 1 ? 'instock' : 'outofstock',
stock_quantity: item.inventory_quantity,
images: (item.images || []).map((img: any) => ({
id: img.id || 0,
src: img.src,
name: '',
alt: img.alt || '',
// 排序
position: img.position || '',
})),
attributes: (item.options || []).map(option => ({
id: option.id || 0,
name: option.option_name || '',
options: (option.values || []).map(value => value.option_value || ''),
})),
tags: (item.tags || []).map((t: any) => ({
id: t.id || 0,
name: t.name || '',
})),
// shopyy叫做专辑
categories: item.collections.map((c: any) => ({
id: c.id || 0,
name: c.title || '',
})),
variations: item.variants?.map(this.mapVariation.bind(this)) || [],
permalink: item.permalink,
date_created:
typeof item.created_at === 'number'
? new Date(item.created_at * 1000).toISOString()
: String(item.created_at ?? ''),
date_modified:
typeof item.updated_at === 'number'
? new Date(item.updated_at * 1000).toISOString()
: String(item.updated_at ?? ''),
raw: item,
};
}
private mapVariation(variant: ShopyyVariant): UnifiedProductVariationDTO {
// 映射变体
return {
id: variant.id,
name: variant.sku || '',
sku: variant.sku || '',
regular_price: String(variant.price ?? ''),
sale_price: String(variant.special_price ?? ''),
price: String(variant.price ?? ''),
stock_status:
variant.inventory_tracking === 1 ? 'instock' : 'outofstock',
stock_quantity: variant.inventory_quantity,
};
}
shopyyOrderStatusMap = {//订单状态 100 未完成110 待处理180 已完成(确认收货); 190 取消;
[100]: OrderStatus.PENDING, // 100 未完成 转为 pending
[110]: OrderStatus.PROCESSING, // 110 待处理 转为 processing
// 已发货
[180]: OrderStatus.COMPLETED, // 180 已完成(确认收货) 转为 completed
[190]: OrderStatus.CANCEL // 190 取消 转为 cancelled
}
private mapOrder(item: ShopyyOrder): UnifiedOrderDTO {
// 提取账单和送货地址 如果不存在则为空对象
const billing = (item as any).billing_address || {};
const shipping = (item as any).shipping_address || {};
// 构建账单地址对象
const billingObj: UnifiedAddressDTO = {
first_name: billing.first_name || item.firstname || '',
last_name: billing.last_name || item.lastname || '',
fullname: billing.name || `${item.firstname} ${item.lastname}`.trim(),
company: billing.company || '',
email: item.customer_email || item.email || '',
phone: billing.phone || (item as any).telephone || '',
address_1: billing.address1 || item.payment_address || '',
address_2: billing.address2 || '',
city: billing.city || item.payment_city || '',
state: billing.province || item.payment_zone || '',
postcode: billing.zip || item.payment_postcode || '',
method_title: item.payment_method || '',
country:
billing.country_name ||
billing.country_code ||
item.payment_country ||
'',
};
// 构建送货地址对象
const shippingObj: UnifiedAddressDTO = {
first_name: shipping.first_name || item.firstname || '',
last_name: shipping.last_name || item.lastname || '',
fullname: shipping.name || '',
company: shipping.company || '',
address_1:
shipping.address1 ||
(typeof item.shipping_address === 'string'
? item.shipping_address
: '') ||
'',
address_2: shipping.address2 || '',
city: shipping.city || item.shipping_city || '',
state: shipping.province || item.shipping_zone || '',
postcode: shipping.zip || item.shipping_postcode || '',
method_title: item.payment_method || '',
country:
shipping.country_name ||
shipping.country_code ||
item.shipping_country ||
'',
};
// 构建送货地址对象
const shipping_lines: UnifiedShippingLineDTO[] = [
{
id: item.fulfillments?.[0]?.id || 0,
method_title: item.payment_method || '',
method_id: item.payment_method || '',
total: Number(item.current_shipping_price).toExponential(2) || '0.00',
total_tax: '0.00',
taxes: [],
meta_data: [],
},
];
// 格式化地址为字符串
const formatAddress = (addr: UnifiedAddressDTO) => {
return [
addr.fullname,
addr.company,
addr.address_1,
addr.address_2,
addr.city,
addr.state,
addr.postcode,
addr.country,
addr.phone,
]
.filter(Boolean)
.join(', ');
};
const lineItems: UnifiedOrderLineItemDTO[] = (item.products || []).map(
(p: any) => ({
id: p.id,
name: p.product_title || p.name,
product_id: p.product_id,
quantity: p.quantity,
total: String(p.price ?? ''),
sku: p.sku || p.sku_code || '',
price: String(p.price ?? ''),
})
);
// 货币符号
const currencySymbols: Record<string, string> = {
'EUR': '€',
'USD': '$',
'GBP': '£',
'JPY': '¥',
'AUD': 'A$',
'CAD': 'C$',
'CHF': 'CHF',
'CNY': '¥',
'HKD': 'HK$',
'NZD': 'NZ$',
'SGD': 'S$'
// 可以根据需要添加更多货币代码和符号
};
// 映射订单状态,如果不存在则默认 pending
const status = this.shopyyOrderStatusMap[item.status ?? item.order_status] || OrderStatus.PENDING;
const finalcial_status = this.shopyyFinancialStatusMap[item.financial_status]
// 发货状态
const fulfillment_status = this.shopyyFulfillmentStatusMap[item.fulfillment_status];
return {
id: item.id || item.order_id,
number: item.order_number || item.order_sn,
status,
financial_status: finalcial_status,
currency: item.currency_code || item.currency,
total: String(item.total_price ?? item.total_amount ?? ''),
customer_id: item.customer_id || item.user_id,
customer_name:
item.customer_name || `${item.firstname} ${item.lastname}`.trim(),
email: item.customer_email || item.email,
customer_email: item.customer_email || item.email,
line_items: lineItems,
sales: lineItems, // 兼容前端
billing: billingObj,
shipping: shippingObj,
billing_full_address: formatAddress(billingObj),
shipping_full_address: formatAddress(shippingObj),
payment_method: item.payment_method,
shipping_lines: shipping_lines || [],
fee_lines: item.fee_lines || [],
coupon_lines: item.coupon_lines || [],
customer_ip_address: item.ip || '',
device_type: item.source_device || '',
utm_source: item.utm_source || '',
source_type: 'shopyy', // FIXME
date_paid: typeof item.pay_at === 'number'
? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString()
: null,
refunds: [],
currency_symbol: (currencySymbols[item.currency] || '$') || '',
date_created:
typeof item.created_at === 'number'
? new Date(item.created_at * 1000).toISOString()
: item.date_added ||
(typeof item.created_at === 'string' ? item.created_at : ''),
date_modified:
typeof item.updated_at === 'number'
? new Date(item.updated_at * 1000).toISOString()
: item.date_updated ||
item.last_modified ||
(typeof item.updated_at === 'string' ? item.updated_at : ''),
fulfillment_status,
fulfillments: item.fulfillments?.map?.((f) => ({
id: f.id,
tracking_number: f.tracking_number || '',
shipping_provider: f.tracking_company || '',
shipping_method: f.tracking_company || '',
date_created: typeof f.created_at === 'number'
? new Date(f.created_at * 1000).toISOString()
: f.created_at || '',
// status: f.payment_tracking_status
})) || [],
raw: item,
};
}
shopyyFulfillmentStatusMap = {
// 未发货
'300': OrderFulfillmentStatus.PENDING,
// 部分发货
'310': OrderFulfillmentStatus.PARTIALLY_FULFILLED,
// 已发货
'320': OrderFulfillmentStatus.FULFILLED,
// 已取消
'330': OrderFulfillmentStatus.CANCELLED,
// 确认发货
}
private mapCustomer(item: ShopyyCustomer): UnifiedCustomerDTO {
// 处理多地址结构
const addresses = item.addresses || [];
const defaultAddress = item.default_address || (addresses.length > 0 ? addresses[0] : {});
// 尝试从地址列表中获取billing和shipping
// 如果没有明确区分,默认使用默认地址或第一个地址
const billingAddress = defaultAddress;
const shippingAddress = defaultAddress;
const billing = {
first_name: billingAddress.first_name || item.first_name || '',
last_name: billingAddress.last_name || item.last_name || '',
fullname: billingAddress.name || `${billingAddress.first_name || item.first_name || ''} ${billingAddress.last_name || item.last_name || ''}`.trim(),
company: billingAddress.company || '',
email: item.email || '',
phone: billingAddress.phone || item.contact || '',
address_1: billingAddress.address1 || '',
address_2: billingAddress.address2 || '',
city: billingAddress.city || '',
state: billingAddress.province || '',
postcode: billingAddress.zip || '',
country: billingAddress.country_name || billingAddress.country_code || item.country?.country_name || ''
};
const shipping = {
first_name: shippingAddress.first_name || item.first_name || '',
last_name: shippingAddress.last_name || item.last_name || '',
fullname: shippingAddress.name || `${shippingAddress.first_name || item.first_name || ''} ${shippingAddress.last_name || item.last_name || ''}`.trim(),
company: shippingAddress.company || '',
address_1: shippingAddress.address1 || '',
address_2: shippingAddress.address2 || '',
city: shippingAddress.city || '',
state: shippingAddress.province || '',
postcode: shippingAddress.zip || '',
country: shippingAddress.country_name || shippingAddress.country_code || item.country?.country_name || ''
};
return {
id: item.id || item.customer_id,
orders: Number(item.orders_count ?? item.order_count ?? item.orders ?? 0),
total_spend: Number(item.total_spent ?? item.total_spend_amount ?? item.total_spend_money ?? 0),
first_name: item.first_name || item.firstname || '',
last_name: item.last_name || item.lastname || '',
fullname: item.fullname || item.customer_name || `${item.first_name || item.firstname || ''} ${item.last_name || item.lastname || ''}`.trim(),
email: item.email || item.customer_email || '',
phone: item.contact || billing.phone || item.phone || '',
billing,
shipping,
date_created:
typeof item.created_at === 'number'
? new Date(item.created_at * 1000).toISOString()
: (typeof item.created_at === 'string' ? item.created_at : item.date_added || ''),
date_modified:
typeof item.updated_at === 'number'
? new Date(item.updated_at * 1000).toISOString()
: (typeof item.updated_at === 'string' ? item.updated_at : item.date_updated || ''),
raw: item,
};
}
async getProducts(
params: UnifiedSearchParamsDTO
): Promise<UnifiedPaginationDTO<UnifiedProductDTO>> {
const response = await this.shopyyService.fetchResourcePaged<ShopyyProduct>(
this.site,
'products/list',
params
);
const { items = [], total, totalPages, page, per_page } = response;
const finalItems = items.map((item) => ({
...item,
permalink: `${this.site.websiteUrl}/products/${item.handle}`,
})).map(this.mapProduct.bind(this))
return {
items: finalItems as UnifiedProductDTO[],
total,
totalPages,
page,
per_page,
};
}
async getAllProducts(params?: UnifiedSearchParamsDTO): Promise<UnifiedProductDTO[]> {
// Shopyy getAllProducts 暂未实现
throw new Error('Shopyy getAllProducts 暂未实现');
}
async getProduct(id: string | number): Promise<UnifiedProductDTO> {
// 使用ShopyyService获取单个产品
const product = await this.shopyyService.getProduct(this.site, id);
return this.mapProduct(product);
}
async createProduct(data: Partial<UnifiedProductDTO>): Promise<UnifiedProductDTO> {
const res = await this.shopyyService.createProduct(this.site, data);
return this.mapProduct(res);
}
async updateProduct(id: string | number, data: Partial<UnifiedProductDTO>): Promise<boolean> {
// Shopyy update returns boolean?
// shopyyService.updateProduct returns boolean.
// So I can't return the updated product.
// I have to fetch it again or return empty/input.
// Since getProduct is missing, I'll return input data as UnifiedProductDTO (mock).
await this.shopyyService.updateProduct(this.site, String(id), data);
return true;
}
async updateVariation(productId: string | number, variationId: string | number, data: any): Promise<any> {
await this.shopyyService.updateVariation(this.site, String(productId), String(variationId), data);
return { ...data, id: variationId };
}
async getOrderNotes(orderId: string | number): Promise<any[]> {
return await this.shopyyService.getOrderNotes(this.site, orderId);
}
async createOrderNote(orderId: string | number, data: any): Promise<any> {
return await this.shopyyService.createOrderNote(this.site, orderId, data);
}
async deleteProduct(id: string | number): Promise<boolean> {
// Use batch delete
await this.shopyyService.batchProcessProducts(this.site, { delete: [id] });
return true;
}
async batchProcessProducts(
data: { create?: any[]; update?: any[]; delete?: Array<string | number> }
): Promise<any> {
return await this.shopyyService.batchProcessProducts(this.site, data);
}
mapUnifiedOrderQueryToShopyyQuery(params: UnifiedSearchParamsDTO) {
const { where = {} as any, ...restParams } = params || {}
const statusMap = {
'pending': '100', // 100 未完成
'processing': '110', // 110 待处理
'completed': "180", // 180 已完成(确认收货)
'cancelled': '190', // 190 取消
}
const normalizedParams: any = {
...restParams,
}
if (where) {
normalizedParams.where = {
...where,
}
if (where.status) {
normalizedParams.where.status = statusMap[where.status];
}
}
return normalizedParams
}
async getOrders(
params: UnifiedSearchParamsDTO
): Promise<UnifiedPaginationDTO<UnifiedOrderDTO>> {
const normalizedParams = this.mapUnifiedOrderQueryToShopyyQuery(params);
const { items, total, totalPages, page, per_page } =
await this.shopyyService.fetchResourcePaged<any>(
this.site,
'orders',
normalizedParams
);
return {
items: items.map(this.mapOrder.bind(this)),
total,
totalPages,
page,
per_page,
};
}
async getAllOrders(params?: UnifiedSearchParamsDTO): Promise<UnifiedOrderDTO[]> {
const data = await this.shopyyService.getAllOrders(this.site.id, params);
return data.map(this.mapOrder.bind(this));
}
async getOrder(id: string | number): Promise<UnifiedOrderDTO> {
const data = await this.shopyyService.getOrder(this.site.id, String(id));
return this.mapOrder(data);
}
async createOrder(data: Partial<UnifiedOrderDTO>): Promise<UnifiedOrderDTO> {
const createdOrder = await this.shopyyService.createOrder(this.site, data);
return this.mapOrder(createdOrder);
}
async updateOrder(id: string | number, data: Partial<UnifiedOrderDTO>): Promise<boolean> {
return await this.shopyyService.updateOrder(this.site, String(id), data);
}
async deleteOrder(id: string | number): Promise<boolean> {
return await this.shopyyService.deleteOrder(this.site, id);
}
async fulfillOrder(orderId: string | number, data: {
tracking_number?: string;
shipping_provider?: string;
shipping_method?: string;
items?: Array<{
order_item_id: number;
quantity: number;
}>;
}): Promise<any> {
// 订单履行(发货)
try {
// 判断是否为部分发货(包含 items
if (data.items && data.items.length > 0) {
// 部分发货
const partShipData = {
order_number: String(orderId),
note: data.shipping_method || '',
tracking_company: data.shipping_provider || '',
tracking_number: data.tracking_number || '',
courier_code: '1', // 默认快递公司代码
products: data.items.map(item => ({
quantity: item.quantity,
order_product_id: String(item.order_item_id)
}))
};
return await this.shopyyService.partFulfillOrder(this.site, partShipData);
} else {
// 批量发货(完整发货)
const batchShipData = {
order_number: String(orderId),
tracking_company: data.shipping_provider || '',
tracking_number: data.tracking_number || '',
courier_code: 1, // 默认快递公司代码
note: data.shipping_method || '',
mode: null // 新增模式
};
return await this.shopyyService.batchFulfillOrders(this.site, batchShipData);
}
} catch (error) {
throw new Error(`履行失败: ${error.message}`);
}
}
async cancelFulfillment(orderId: string | number, data: {
reason?: string;
shipment_id?: string;
}): Promise<any> {
// 取消订单履行
try {
// 调用 ShopyyService 的取消履行方法
const cancelShipData = {
order_id: String(orderId),
fulfillment_id: data.shipment_id || ''
};
const result = await this.shopyyService.cancelFulfillment(this.site, cancelShipData);
return {
success: result,
order_id: orderId,
shipment_id: data.shipment_id,
reason: data.reason,
cancelled_at: new Date().toISOString()
};
} catch (error) {
throw new Error(`取消履行失败: ${error.message}`);
}
}
/**
* 获取订单履行信息
* @param orderId 订单ID
* @returns 履行信息列表
*/
async getOrderFulfillments(orderId: string | number): Promise<any[]> {
return await this.shopyyService.getFulfillments(this.site, String(orderId));
}
/**
* 创建订单履行信息
* @param orderId 订单ID
* @param data 履行数据
* @returns 创建结果
*/
async createOrderFulfillment(orderId: string | number, data: FulfillmentDTO): Promise<any> {
// 调用 Shopyy Service 的 createFulfillment 方法
const fulfillmentData = {
tracking_number: data.tracking_number,
carrier_code: data.shipping_provider,
carrier_name: data.shipping_provider,
shipping_method: data.shipping_method || 'standard'
};
return await this.shopyyService.createFulfillment(this.site, String(orderId), fulfillmentData);
}
/**
* 更新订单履行信息
* @param orderId 订单ID
* @param fulfillmentId 履行ID
* @param data 更新数据
* @returns 更新结果
*/
async updateOrderFulfillment(orderId: string | number, fulfillmentId: string, data: {
tracking_number?: string;
tracking_provider?: string;
date_shipped?: string;
status_shipped?: string;
}): Promise<any> {
return await this.shopyyService.updateFulfillment(this.site, String(orderId), fulfillmentId, data);
}
/**
* 删除订单履行信息
* @param orderId 订单ID
* @param fulfillmentId 履行ID
* @returns 删除结果
*/
async deleteOrderFulfillment(orderId: string | number, fulfillmentId: string): Promise<boolean> {
return await this.shopyyService.deleteFulfillment(this.site, String(orderId), fulfillmentId);
}
async getSubscriptions(
params: UnifiedSearchParamsDTO
): Promise<UnifiedPaginationDTO<UnifiedSubscriptionDTO>> {
throw new Error('Shopyy does not support subscriptions.');
}
async getAllSubscriptions(params?: UnifiedSearchParamsDTO): Promise<UnifiedSubscriptionDTO[]> {
// Shopyy getAllSubscriptions 暂未实现
throw new Error('Shopyy getAllSubscriptions 暂未实现');
}
async getMedia(
params: UnifiedSearchParamsDTO
): Promise<UnifiedPaginationDTO<UnifiedMediaDTO>> {
const requestParams = this.mapMediaSearchParams(params);
const { items, total, totalPages, page, per_page } = await this.shopyyService.fetchResourcePaged<any>(
this.site,
'media', // Shopyy的媒体API端点可能需要调整
requestParams
);
return {
items: items.map(this.mapMedia.bind(this)),
total,
totalPages,
page,
per_page,
};
}
async getAllMedia(params?: UnifiedSearchParamsDTO): Promise<UnifiedMediaDTO[]> {
// Shopyy getAllMedia 暂未实现
throw new Error('Shopyy getAllMedia 暂未实现');
}
async createMedia(file: any): Promise<UnifiedMediaDTO> {
const createdMedia = await this.shopyyService.createMedia(this.site, file);
return this.mapMedia(createdMedia);
}
async updateMedia(id: string | number, data: any): Promise<UnifiedMediaDTO> {
const updatedMedia = await this.shopyyService.updateMedia(this.site, id, data);
return this.mapMedia(updatedMedia);
}
async deleteMedia(id: string | number): Promise<boolean> {
return await this.shopyyService.deleteMedia(this.site, id);
}
async getReviews(
params: UnifiedSearchParamsDTO
): Promise<UnifiedReviewPaginationDTO> {
const requestParams = this.mapReviewSearchParams(params);
const { items, total, totalPages, page, per_page } = await this.shopyyService.getReviews(
this.site,
requestParams
);
return {
items: items.map(this.mapReview),
total,
totalPages,
page,
per_page,
};
}
async getAllReviews(params?: UnifiedSearchParamsDTO): Promise<UnifiedReviewDTO[]> {
// Shopyy getAllReviews 暂未实现
throw new Error('Shopyy getAllReviews 暂未实现');
}
async getReview(id: string | number): Promise<UnifiedReviewDTO> {
const review = await this.shopyyService.getReview(this.site, id);
return this.mapReview(review);
}
private mapReview(review: any): UnifiedReviewDTO {
// 将ShopYY评论数据映射到统一评论DTO格式
return {
id: review.id || review.review_id,
product_id: review.product_id || review.goods_id,
author: review.author_name || review.username || '',
email: review.author_email || review.user_email || '',
content: review.comment || review.content || '',
rating: Number(review.score || review.rating || 0),
status: String(review.status || 'approved'),
date_created:
typeof review.created_at === 'number'
? new Date(review.created_at * 1000).toISOString()
: String(review.created_at || review.date_added || '')
};
}
private mapReviewSearchParams(params: UnifiedSearchParamsDTO): any {
const { search, page, per_page, where } = params;
const shopyyParams: any = {
page: page || 1,
limit: per_page || 10,
};
if (search) {
shopyyParams.search = search;
}
if (where.status) {
shopyyParams.status = where.status;
}
// if (product_id) {
// shopyyParams.product_id = product_id;
// }
return shopyyParams;
}
async createReview(data: any): Promise<UnifiedReviewDTO> {
const createdReview = await this.shopyyService.createReview(this.site, data);
return this.mapReview(createdReview);
}
async updateReview(id: string | number, data: any): Promise<UnifiedReviewDTO> {
const updatedReview = await this.shopyyService.updateReview(this.site, id, data);
return this.mapReview(updatedReview);
}
async deleteReview(id: string | number): Promise<boolean> {
return await this.shopyyService.deleteReview(this.site, id);
}
// Webhook相关方法
private mapWebhook(item: ShopyyWebhook): UnifiedWebhookDTO {
return {
id: item.id,
name: item.webhook_name || `Webhook-${item.id}`,
topic: item.event_code || '',
delivery_url: item.url || '',
status: 'active',
};
}
async getWebhooks(params: UnifiedSearchParamsDTO): Promise<UnifiedWebhookPaginationDTO> {
const { items, total, totalPages, page, per_page } = await this.shopyyService.getWebhooks(this.site, params);
return {
items: items.map(this.mapWebhook),
total,
totalPages,
page,
per_page,
};
}
async getAllWebhooks(params?: UnifiedSearchParamsDTO): Promise<UnifiedWebhookDTO[]> {
// Shopyy getAllWebhooks 暂未实现
throw new Error('Shopyy getAllWebhooks 暂未实现');
}
async getWebhook(id: string | number): Promise<UnifiedWebhookDTO> {
const webhook = await this.shopyyService.getWebhook(this.site, id);
return this.mapWebhook(webhook);
}
async createWebhook(data: CreateWebhookDTO): Promise<UnifiedWebhookDTO> {
const createdWebhook = await this.shopyyService.createWebhook(this.site, data);
return this.mapWebhook(createdWebhook);
}
async updateWebhook(id: string | number, data: UpdateWebhookDTO): Promise<UnifiedWebhookDTO> {
const updatedWebhook = await this.shopyyService.updateWebhook(this.site, id, data);
return this.mapWebhook(updatedWebhook);
}
async deleteWebhook(id: string | number): Promise<boolean> {
return await this.shopyyService.deleteWebhook(this.site, id);
}
async getLinks(): Promise<Array<{ title: string, url: string }>> {
// ShopYY站点的管理后台链接通常基于apiUrl构建
const url = this.site.websiteUrl
// 提取基础域名,去掉可能的路径部分
const baseUrl = url.replace(/\/api\/.*$/i, '');
const links = [
{ title: '访问网站', url: baseUrl },
{ title: '管理后台', url: `${baseUrl}/admin/` },
{ title: '订单管理', url: `${baseUrl}/admin/orders.htm` },
{ title: '产品管理', url: `${baseUrl}/admin/products.htm` },
{ title: '客户管理', url: `${baseUrl}/admin/customers.htm` },
{ title: '插件管理', url: `${baseUrl}/admin/apps.htm` },
{ title: '店铺设置', url: `${baseUrl}/admin/settings.htm` },
{ title: '营销中心', url: `${baseUrl}/admin/marketing.htm` },
];
return links;
}
async getCustomers(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedCustomerDTO>> {
const { items, total, totalPages, page, per_page } =
await this.shopyyService.fetchCustomersPaged(this.site, params);
return {
items: items.map(this.mapCustomer.bind(this)),
total,
totalPages,
page,
per_page
};
}
async getAllCustomers(params?: UnifiedSearchParamsDTO): Promise<UnifiedCustomerDTO[]> {
// Shopyy getAllCustomers 暂未实现
throw new Error('Shopyy getAllCustomers 暂未实现');
}
async getCustomer(id: string | number): Promise<UnifiedCustomerDTO> {
const customer = await this.shopyyService.getCustomer(this.site, id);
return this.mapCustomer(customer);
}
async createCustomer(data: Partial<UnifiedCustomerDTO>): Promise<UnifiedCustomerDTO> {
const createdCustomer = await this.shopyyService.createCustomer(this.site, data);
return this.mapCustomer(createdCustomer);
}
async updateCustomer(id: string | number, data: Partial<UnifiedCustomerDTO>): Promise<UnifiedCustomerDTO> {
const updatedCustomer = await this.shopyyService.updateCustomer(this.site, id, data);
return this.mapCustomer(updatedCustomer);
}
async deleteCustomer(id: string | number): Promise<boolean> {
return await this.shopyyService.deleteCustomer(this.site, id);
}
async getVariations(productId: string | number, params: UnifiedSearchParamsDTO): Promise<any> {
throw new Error('Shopyy getVariations 暂未实现');
}
async getAllVariations(productId: string | number, params?: UnifiedSearchParamsDTO): Promise<UnifiedProductVariationDTO[]> {
throw new Error('Shopyy getAllVariations 暂未实现');
}
async getVariation(productId: string | number, variationId: string | number): Promise<UnifiedProductVariationDTO> {
throw new Error('Shopyy getVariation 暂未实现');
}
async createVariation(productId: string | number, data: any): Promise<UnifiedProductVariationDTO> {
throw new Error('Shopyy createVariation 暂未实现');
}
async deleteVariation(productId: string | number, variationId: string | number): Promise<boolean> {
throw new Error('Shopyy deleteVariation 暂未实现');
}
}