714 lines
25 KiB
TypeScript
714 lines
25 KiB
TypeScript
import { ISiteAdapter } from '../interface/site-adapter.interface';
|
|
import { ShopyyService } from '../service/shopyy.service';
|
|
import {
|
|
UnifiedAddressDTO,
|
|
UnifiedCustomerDTO,
|
|
UnifiedMediaDTO,
|
|
UnifiedOrderDTO,
|
|
UnifiedOrderLineItemDTO,
|
|
UnifiedPaginationDTO,
|
|
UnifiedProductDTO,
|
|
UnifiedProductVariationDTO,
|
|
UnifiedSearchParamsDTO,
|
|
UnifiedSubscriptionDTO,
|
|
UnifiedReviewPaginationDTO,
|
|
UnifiedReviewDTO,
|
|
UnifiedWebhookDTO,
|
|
UnifiedWebhookPaginationDTO,
|
|
CreateWebhookDTO,
|
|
UpdateWebhookDTO,
|
|
} from '../dto/site-api.dto';
|
|
import {
|
|
ShopyyCustomer,
|
|
ShopyyOrder,
|
|
ShopyyProduct,
|
|
ShopyyVariant,
|
|
ShopyyWebhook,
|
|
} from '../dto/shopyy.dto';
|
|
|
|
export class ShopyyAdapter implements ISiteAdapter {
|
|
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,
|
|
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,
|
|
};
|
|
}
|
|
|
|
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 || '',
|
|
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 || '',
|
|
country:
|
|
shipping.country_name ||
|
|
shipping.country_code ||
|
|
item.shipping_country ||
|
|
'',
|
|
};
|
|
|
|
// 格式化地址为字符串
|
|
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 || '',
|
|
})
|
|
);
|
|
|
|
return {
|
|
id: item.id || item.order_id,
|
|
number: item.order_number || item.order_sn,
|
|
status: String(item.status || item.order_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,
|
|
line_items: lineItems,
|
|
sales: lineItems, // 兼容前端
|
|
billing: billingObj,
|
|
shipping: shippingObj,
|
|
billing_full_address: formatAddress(billingObj),
|
|
shipping_full_address: formatAddress(shippingObj),
|
|
payment_method: item.payment_method,
|
|
refunds: [],
|
|
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 : ''),
|
|
raw: item,
|
|
};
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
async getOrders(
|
|
params: UnifiedSearchParamsDTO
|
|
): Promise<UnifiedPaginationDTO<UnifiedOrderDTO>> {
|
|
const { items, total, totalPages, page, per_page } =
|
|
await this.shopyyService.fetchResourcePaged<any>(
|
|
this.site,
|
|
'orders',
|
|
params
|
|
);
|
|
return {
|
|
items: items.map(this.mapOrder.bind(this)),
|
|
total,
|
|
totalPages,
|
|
page,
|
|
per_page,
|
|
};
|
|
}
|
|
|
|
async getAllOrders(params?: UnifiedSearchParamsDTO): Promise<UnifiedOrderDTO[]> {
|
|
// Shopyy getAllOrders 暂未实现
|
|
throw new Error('Shopyy getAllOrders 暂未实现');
|
|
}
|
|
|
|
async getOrder(id: string | number): Promise<UnifiedOrderDTO> {
|
|
const data = await this.shopyyService.getOrder(String(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 shipOrder(orderId: string | number, data: {
|
|
tracking_number?: string;
|
|
shipping_provider?: string;
|
|
shipping_method?: string;
|
|
items?: Array<{
|
|
order_item_id: number;
|
|
quantity: number;
|
|
}>;
|
|
}): Promise<any> {
|
|
// 订单发货
|
|
try {
|
|
// 更新订单状态为已发货
|
|
await this.shopyyService.updateOrder(this.site, String(orderId), {
|
|
status: 'completed',
|
|
meta_data: [
|
|
{ key: '_tracking_number', value: data.tracking_number },
|
|
{ key: '_shipping_provider', value: data.shipping_provider },
|
|
{ key: '_shipping_method', value: data.shipping_method }
|
|
]
|
|
});
|
|
|
|
// 添加发货备注
|
|
const note = `订单已发货${data.tracking_number ? `,物流单号:${data.tracking_number}` : ''}${data.shipping_provider ? `,物流公司:${data.shipping_provider}` : ''}`;
|
|
await this.shopyyService.createOrderNote(this.site, orderId, { note, customer_note: true });
|
|
|
|
return {
|
|
success: true,
|
|
order_id: orderId,
|
|
shipment_id: `shipment_${orderId}_${Date.now()}`,
|
|
tracking_number: data.tracking_number,
|
|
shipping_provider: data.shipping_provider,
|
|
shipped_at: new Date().toISOString()
|
|
};
|
|
} catch (error) {
|
|
throw new Error(`发货失败: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async cancelShipOrder(orderId: string | number, data: {
|
|
reason?: string;
|
|
shipment_id?: string;
|
|
}): Promise<any> {
|
|
// 取消订单发货
|
|
try {
|
|
// 将订单状态改回处理中
|
|
await this.shopyyService.updateOrder(this.site, String(orderId), {
|
|
status: 'processing',
|
|
meta_data: [
|
|
{ key: '_shipment_cancelled', value: 'yes' },
|
|
{ key: '_shipment_cancelled_reason', value: data.reason }
|
|
]
|
|
});
|
|
|
|
// 添加取消发货的备注
|
|
const note = `订单发货已取消${data.reason ? `,原因:${data.reason}` : ''}`;
|
|
await this.shopyyService.createOrderNote(this.site, orderId, { note, customer_note: true });
|
|
|
|
return {
|
|
success: true,
|
|
order_id: orderId,
|
|
shipment_id: data.shipment_id,
|
|
reason: data.reason,
|
|
cancelled_at: new Date().toISOString()
|
|
};
|
|
} catch (error) {
|
|
throw new Error(`取消发货失败: ${error.message}`);
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|