1298 lines
49 KiB
TypeScript
1298 lines
49 KiB
TypeScript
import { ISiteAdapter } from '../interface/site-adapter.interface';
|
||
import {
|
||
UnifiedMediaDTO,
|
||
UnifiedOrderDTO,
|
||
UnifiedProductDTO,
|
||
UnifiedSubscriptionDTO,
|
||
UnifiedCustomerDTO,
|
||
UnifiedReviewPaginationDTO,
|
||
UnifiedReviewDTO,
|
||
UnifiedWebhookDTO,
|
||
UnifiedWebhookPaginationDTO,
|
||
CreateWebhookDTO,
|
||
UpdateWebhookDTO,
|
||
CreateVariationDTO,
|
||
UpdateVariationDTO,
|
||
UnifiedProductVariationDTO,
|
||
UnifiedVariationPaginationDTO,
|
||
} from '../dto/site-api.dto';
|
||
import { UnifiedPaginationDTO, UnifiedSearchParamsDTO } from '../dto/api.dto';
|
||
import {
|
||
WooProduct,
|
||
WooOrder,
|
||
WooSubscription,
|
||
WpMedia,
|
||
WooCustomer,
|
||
WooWebhook,
|
||
WooOrderSearchParams,
|
||
WooProductSearchParams,
|
||
} from '../dto/woocommerce.dto';
|
||
import { Site } from '../entity/site.entity';
|
||
import { WPService } from '../service/wp.service';
|
||
|
||
export class WooCommerceAdapter implements ISiteAdapter {
|
||
// 构造函数接收站点配置与服务实例
|
||
constructor(private site: Site, private wpService: WPService) {
|
||
this.mapProduct = this.mapProduct.bind(this);
|
||
this.mapReview = this.mapReview.bind(this);
|
||
this.mapCustomer = this.mapCustomer.bind(this);
|
||
this.mapMedia = this.mapMedia.bind(this);
|
||
this.mapOrder = this.mapOrder.bind(this);
|
||
this.mapWebhook = this.mapWebhook.bind(this);
|
||
}
|
||
|
||
// 映射 WooCommerce webhook 到统一格式
|
||
mapWebhook(webhook: WooWebhook): UnifiedWebhookDTO {
|
||
return {
|
||
id: webhook.id.toString(),
|
||
name: webhook.name,
|
||
status: webhook.status,
|
||
topic: webhook.topic,
|
||
delivery_url: webhook.delivery_url,
|
||
secret: webhook.secret,
|
||
api_version: webhook.api_version,
|
||
date_created: webhook.date_created,
|
||
date_modified: webhook.date_modified,
|
||
// metadata: webhook.meta_data || [],
|
||
};
|
||
}
|
||
|
||
// 获取站点的 webhooks 列表
|
||
async getWebhooks(params: UnifiedSearchParamsDTO): Promise<UnifiedWebhookPaginationDTO> {
|
||
try {
|
||
const result = await this.wpService.getWebhooks(this.site, params);
|
||
|
||
return {
|
||
items: (result.items as WooWebhook[]).map(this.mapWebhook),
|
||
total: result.total,
|
||
page: Number(params.page || 1),
|
||
per_page: Number(params.per_page || 20),
|
||
totalPages: result.totalPages,
|
||
};
|
||
} catch (error) {
|
||
throw new Error(`Failed to get webhooks: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 获取所有webhooks
|
||
async getAllWebhooks(params?: UnifiedSearchParamsDTO): Promise<UnifiedWebhookDTO[]> {
|
||
try {
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const webhooks = await this.wpService.sdkGetAll(api, 'webhooks', params);
|
||
return webhooks.map((webhook: any) => this.mapWebhook(webhook));
|
||
} catch (error) {
|
||
throw new Error(`Failed to get all webhooks: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 获取单个 webhook 详情
|
||
async getWebhook(id: string | number): Promise<UnifiedWebhookDTO> {
|
||
try {
|
||
const result = await this.wpService.getWebhook(this.site, id);
|
||
return this.mapWebhook(result as WooWebhook);
|
||
} catch (error) {
|
||
throw new Error(`Failed to get webhook: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 创建新的 webhook
|
||
async createWebhook(data: CreateWebhookDTO): Promise<UnifiedWebhookDTO> {
|
||
try {
|
||
const params = {
|
||
name: data.name,
|
||
status: 'active', // 默认状态为活跃
|
||
topic: data.topic,
|
||
delivery_url: data.delivery_url,
|
||
secret: data.secret,
|
||
api_version: data.api_version || 'wp/v2',
|
||
};
|
||
const result = await this.wpService.createWebhook(this.site, params);
|
||
return this.mapWebhook(result as WooWebhook);
|
||
} catch (error) {
|
||
throw new Error(`Failed to create webhook: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 更新现有的 webhook
|
||
async updateWebhook(id: string | number, data: UpdateWebhookDTO): Promise<UnifiedWebhookDTO> {
|
||
try {
|
||
const params = {
|
||
...(data.name ? { name: data.name } : {}),
|
||
...(data.status ? { status: data.status } : {}),
|
||
...(data.topic ? { topic: data.topic } : {}),
|
||
...(data.delivery_url ? { delivery_url: data.delivery_url } : {}),
|
||
...(data.secret ? { secret: data.secret } : {}),
|
||
...(data.api_version ? { api_version: data.api_version } : {}),
|
||
};
|
||
const result = await this.wpService.updateWebhook(this.site, id, params);
|
||
return this.mapWebhook(result as WooWebhook);
|
||
} catch (error) {
|
||
throw new Error(`Failed to update webhook: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 删除指定的 webhook
|
||
async deleteWebhook(id: string | number): Promise<boolean> {
|
||
try {
|
||
await this.wpService.deleteWebhook(this.site, id);
|
||
return true;
|
||
} catch (error) {
|
||
throw new Error(`Failed to delete webhook: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
async getLinks(): Promise<Array<{ title: string, url: string }>> {
|
||
const baseUrl = this.site.apiUrl;
|
||
const links = [
|
||
{ title: '访问网站', url: baseUrl },
|
||
{ title: '管理后台', url: `${baseUrl}/wp-admin/` },
|
||
{ title: '订单管理', url: `${baseUrl}/wp-admin/edit.php?post_type=shop_order` },
|
||
{ title: '产品管理', url: `${baseUrl}/wp-admin/edit.php?post_type=product` },
|
||
{ title: '客户管理', url: `${baseUrl}/wp-admin/users.php` },
|
||
{ title: '插件管理', url: `${baseUrl}/wp-admin/plugins.php` },
|
||
{ title: '主题管理', url: `${baseUrl}/wp-admin/themes.php` },
|
||
{ title: 'WooCommerce设置', url: `${baseUrl}/wp-admin/admin.php?page=wc-settings` },
|
||
{ title: 'WooCommerce报告', url: `${baseUrl}/wp-admin/admin.php?page=wc-reports` },
|
||
];
|
||
return links;
|
||
}
|
||
|
||
createMedia(file: any): Promise<UnifiedMediaDTO> {
|
||
throw new Error('Method not implemented.');
|
||
}
|
||
batchProcessOrders?(data: { create?: any[]; update?: any[]; delete?: Array<string | number>; }): Promise<any> {
|
||
throw new Error('Method not implemented.');
|
||
}
|
||
batchProcessCustomers?(data: { create?: any[]; update?: any[]; delete?: Array<string | number>; }): Promise<any> {
|
||
throw new Error('Method not implemented.');
|
||
}
|
||
|
||
|
||
|
||
mapProductSearchParams(params: UnifiedSearchParamsDTO): Partial<WooProductSearchParams> {
|
||
const page = Number(params.page ?? 1);
|
||
const per_page = Number(params.per_page ?? 20);
|
||
const where = params.where && typeof params.where === 'object' ? params.where : {};
|
||
|
||
const mapped: any = {
|
||
...(params.search ? { search: params.search } : {}),
|
||
...(where.status ? { status: where.status } : {}),
|
||
page,
|
||
per_page,
|
||
};
|
||
|
||
const toArray = (value: any): any[] => {
|
||
if (Array.isArray(value)) return value;
|
||
if (value === undefined || value === null) return [];
|
||
return String(value).split(',').map(v => v.trim()).filter(Boolean);
|
||
};
|
||
|
||
if (where.search_fields ?? where.searchFields) mapped.search_fields = toArray(where.search_fields ?? where.searchFields);
|
||
if (where.after ?? where.date_created_after ?? where.created_after) mapped.after = String(where.after ?? where.date_created_after ?? where.created_after);
|
||
if (where.before ?? where.date_created_before ?? where.created_before) mapped.before = String(where.before ?? where.date_created_before ?? where.created_before);
|
||
if (where.modified_after ?? where.date_modified_after) mapped.modified_after = String(where.modified_after ?? where.date_modified_after);
|
||
if (where.modified_before ?? where.date_modified_before) mapped.modified_before = String(where.modified_before ?? where.date_modified_before);
|
||
if (where.dates_are_gmt ?? where.datesAreGmt) mapped.dates_are_gmt = Boolean(where.dates_are_gmt ?? where.datesAreGmt);
|
||
if (where.exclude ?? where.exclude_ids ?? where.excludedIds) mapped.exclude = toArray(where.exclude ?? where.exclude_ids ?? where.excludedIds);
|
||
if (where.include ?? where.ids) mapped.include = toArray(where.include ?? where.ids);
|
||
if (where.offset !== undefined) mapped.offset = Number(where.offset);
|
||
if (where.parent ?? where.parentId) mapped.parent = toArray(where.parent ?? where.parentId);
|
||
if (where.parent_exclude ?? where.parentExclude) mapped.parent_exclude = toArray(where.parent_exclude ?? where.parentExclude);
|
||
if (where.slug) mapped.slug = String(where.slug);
|
||
if (!mapped.status && (where.status || where.include_status || where.exclude_status || where.includeStatus || where.excludeStatus)) {
|
||
if (where.include_status ?? where.includeStatus) mapped.include_status = String(where.include_status ?? where.includeStatus);
|
||
if (where.exclude_status ?? where.excludeStatus) mapped.exclude_status = String(where.exclude_status ?? where.excludeStatus);
|
||
if (where.status) mapped.status = String(where.status);
|
||
}
|
||
if (where.type) mapped.type = String(where.type);
|
||
if (where.include_types ?? where.includeTypes) mapped.include_types = String(where.include_types ?? where.includeTypes);
|
||
if (where.exclude_types ?? where.excludeTypes) mapped.exclude_types = String(where.exclude_types ?? where.excludeTypes);
|
||
if (where.sku) mapped.sku = String(where.sku);
|
||
if (where.featured ?? where.isFeatured) mapped.featured = Boolean(where.featured ?? where.isFeatured);
|
||
if (where.category ?? where.categoryId) mapped.category = String(where.category ?? where.categoryId);
|
||
if (where.tag ?? where.tagId) mapped.tag = String(where.tag ?? where.tagId);
|
||
if (where.shipping_class ?? where.shippingClass) mapped.shipping_class = String(where.shipping_class ?? where.shippingClass);
|
||
if (where.attribute ?? where.attributeName) mapped.attribute = String(where.attribute ?? where.attributeName);
|
||
if (where.attribute_term ?? where.attributeTermId ?? where.attributeTerm) mapped.attribute_term = String(where.attribute_term ?? where.attributeTermId ?? where.attributeTerm);
|
||
if (where.tax_class ?? where.taxClass) mapped.tax_class = String(where.tax_class ?? where.taxClass);
|
||
if (where.on_sale ?? where.onSale) mapped.on_sale = Boolean(where.on_sale ?? where.onSale);
|
||
if (where.min_price ?? where.minPrice) mapped.min_price = String(where.min_price ?? where.minPrice);
|
||
if (where.max_price ?? where.maxPrice) mapped.max_price = String(where.max_price ?? where.maxPrice);
|
||
if (where.stock_status ?? where.stockStatus) mapped.stock_status = String(where.stock_status ?? where.stockStatus);
|
||
if (where.virtual !== undefined) mapped.virtual = Boolean(where.virtual);
|
||
if (where.downloadable !== undefined) mapped.downloadable = Boolean(where.downloadable);
|
||
|
||
return mapped;
|
||
}
|
||
|
||
mapOrderSearchParams(params: UnifiedSearchParamsDTO): Partial<WooOrderSearchParams> {
|
||
// 计算分页参数
|
||
const page = Number(params.page ?? 1);
|
||
const per_page = Number(params.per_page ?? 20);
|
||
// 解析排序参数 支持从 order 对象推导
|
||
const where = params.where && typeof params.where === 'object' ? params.where : {};
|
||
|
||
// if (params.orderBy && typeof params.orderBy === 'object') {
|
||
// }
|
||
const mapped: any = {
|
||
...(params.search ? { search: params.search } : {}),
|
||
// ...(orderBy ? { orderBy } : {}),
|
||
page,
|
||
per_page,
|
||
};
|
||
|
||
const toArray = (value: any): any[] => {
|
||
if (Array.isArray(value)) return value;
|
||
if (value === undefined || value === null) return [];
|
||
return String(value).split(',').map(v => v.trim()).filter(Boolean);
|
||
};
|
||
|
||
const toNumber = (value: any): number | undefined => {
|
||
if (value === undefined || value === null || value === '') return undefined;
|
||
const n = Number(value);
|
||
return Number.isFinite(n) ? n : undefined;
|
||
};
|
||
|
||
// 时间过滤参数
|
||
if (where.after ?? where.date_created_after ?? where.created_after) mapped.after = String(where.after ?? where.date_created_after ?? where.created_after);
|
||
if (where.before ?? where.date_created_before ?? where.created_before) mapped.before = String(where.before ?? where.date_created_before ?? where.created_before);
|
||
if (where.modified_after ?? where.date_modified_after) mapped.modified_after = String(where.modified_after ?? where.date_modified_after);
|
||
if (where.modified_before ?? where.date_modified_before) mapped.modified_before = String(where.modified_before ?? where.date_modified_before);
|
||
if (where.dates_are_gmt ?? where.datesAreGmt) mapped.dates_are_gmt = Boolean(where.dates_are_gmt ?? where.datesAreGmt);
|
||
|
||
// 集合过滤参数
|
||
if (where.exclude) mapped.exclude = toArray(where.exclude);
|
||
if (where.include) mapped.include = toArray(where.include);
|
||
if (where.ids) mapped.include = toArray(where.ids);
|
||
if (toNumber(where.offset) !== undefined) mapped.offset = Number(where.offset);
|
||
if (where.parent ?? where.parentId) mapped.parent = toArray(where.parent ?? where.parentId);
|
||
if (where.parent_exclude ?? where.parentExclude) mapped.parent_exclude = toArray(where.parent_exclude ?? where.parentExclude);
|
||
|
||
// 状态过滤 参数支持数组或逗号分隔字符串
|
||
const statusSource = where.status;
|
||
if (statusSource !== undefined) {
|
||
mapped.status = Array.isArray(statusSource)
|
||
? statusSource.map(s => String(s))
|
||
: String(statusSource).split(',').map(s => s.trim()).filter(Boolean);
|
||
}
|
||
|
||
// 客户与产品过滤
|
||
const customerVal = where.customer ?? where.customer_id;
|
||
const productVal = where.product ?? where.product_id;
|
||
const dpVal = where.dp;
|
||
if (toNumber(customerVal) !== undefined) mapped.customer = Number(customerVal);
|
||
if (toNumber(productVal) !== undefined) mapped.product = Number(productVal);
|
||
if (toNumber(dpVal) !== undefined) mapped.dp = Number(dpVal);
|
||
|
||
// 创建来源过滤 支持逗号分隔
|
||
const createdViaVal = where.created_via;
|
||
if (createdViaVal !== undefined) mapped.created_via = Array.isArray(createdViaVal)
|
||
? createdViaVal.join(',')
|
||
: String(createdViaVal);
|
||
|
||
return mapped;
|
||
}
|
||
|
||
mapCustomerSearchParams(params: UnifiedSearchParamsDTO): Record<string, any> {
|
||
const page = Number(params.page ?? 1);
|
||
const per_page = Number(params.per_page ?? 20);
|
||
const where = params.where && typeof params.where === 'object' ? params.where : {};
|
||
|
||
|
||
const mapped: any = {
|
||
...(params.search ? { search: params.search } : {}),
|
||
page,
|
||
per_page,
|
||
};
|
||
|
||
// 处理orderBy参数,转换为WooCommerce API的order和orderby格式
|
||
if (params.orderBy) {
|
||
// 支持字符串格式 "field:desc" 或对象格式 { "field": "desc" }
|
||
if (typeof params.orderBy === 'string') {
|
||
const [field, direction = 'desc'] = params.orderBy.split(':');
|
||
mapped.orderby = field;
|
||
mapped.order = direction.toLowerCase() === 'asc' ? 'asc' : 'desc';
|
||
} else if (typeof params.orderBy === 'object') {
|
||
const entries = Object.entries(params.orderBy);
|
||
if (entries.length > 0) {
|
||
const [field, direction] = entries[0];
|
||
mapped.orderby = field;
|
||
mapped.order = direction === 'asc' ? 'asc' : 'desc';
|
||
}
|
||
}
|
||
}
|
||
|
||
const toArray = (value: any): any[] => {
|
||
if (Array.isArray(value)) return value;
|
||
if (value === undefined || value === null) return [];
|
||
return String(value).split(',').map(v => v.trim()).filter(Boolean);
|
||
};
|
||
|
||
const toNumber = (value: any): number | undefined => {
|
||
if (value === undefined || value === null || value === '') return undefined;
|
||
const n = Number(value);
|
||
return Number.isFinite(n) ? n : undefined;
|
||
};
|
||
|
||
if (where.exclude) mapped.exclude = toArray(where.exclude);
|
||
if (where.include) mapped.include = toArray(where.include);
|
||
if (where.ids) mapped.include = toArray(where.ids);
|
||
if (toNumber(where.offset) !== undefined) mapped.offset = Number(where.offset);
|
||
|
||
if (where.email) mapped.email = String(where.email);
|
||
const roleSource = where.role;
|
||
if (roleSource !== undefined) mapped.role = String(roleSource);
|
||
|
||
return mapped;
|
||
}
|
||
|
||
mapProduct(item: WooProduct): UnifiedProductDTO {
|
||
// 将 WooCommerce 产品数据映射为统一产品DTO
|
||
// 保留常用字段与时间信息以便前端统一展示
|
||
// https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#product-properties
|
||
|
||
// 映射变体数据
|
||
const mappedVariations = item.variations && Array.isArray(item.variations)
|
||
? item.variations
|
||
.filter((variation: any) => typeof variation !== 'number') // 过滤掉数字类型的变体ID
|
||
.map((variation: any) => {
|
||
// 将变体属性转换为统一格式
|
||
const mappedAttributes = variation.attributes && Array.isArray(variation.attributes)
|
||
? variation.attributes.map((attr: any) => ({
|
||
id: attr.id,
|
||
name: attr.name || '',
|
||
position: attr.position,
|
||
visible: attr.visible,
|
||
variation: attr.variation,
|
||
option: attr.option || '' // 变体属性使用 option 而不是 options
|
||
}))
|
||
: [];
|
||
|
||
// 映射变体图片
|
||
const mappedImage = variation.image
|
||
? {
|
||
id: variation.image.id,
|
||
src: variation.image.src,
|
||
name: variation.image.name,
|
||
alt: variation.image.alt,
|
||
}
|
||
: undefined;
|
||
|
||
return {
|
||
id: variation.id,
|
||
name: variation.name || item.name, // 如果变体没有名称,使用父产品名称
|
||
sku: variation.sku || '',
|
||
regular_price: String(variation.regular_price || ''),
|
||
sale_price: String(variation.sale_price || ''),
|
||
price: String(variation.price || ''),
|
||
stock_status: variation.stock_status || 'outofstock',
|
||
stock_quantity: variation.stock_quantity || 0,
|
||
attributes: mappedAttributes,
|
||
image: mappedImage
|
||
};
|
||
})
|
||
: [];
|
||
|
||
return {
|
||
id: item.id,
|
||
date_created: item.date_created,
|
||
date_modified: item.date_modified,
|
||
type: item.type, // simple grouped external variable
|
||
status: item.status, // draft pending private publish
|
||
sku: item.sku,
|
||
name: item.name,
|
||
//价格
|
||
regular_price: item.regular_price,
|
||
sale_price: item.sale_price,
|
||
price: item.price,
|
||
stock_status: item.stock_status,
|
||
stock_quantity: item.stock_quantity,
|
||
images: (item.images || []).map((img: any) => ({
|
||
id: img.id,
|
||
src: img.src,
|
||
name: img.name,
|
||
alt: img.alt,
|
||
})),
|
||
categories: (item.categories || []).map((c: any) => ({
|
||
id: c.id,
|
||
name: c.name,
|
||
})),
|
||
tags: (item.tags || []).map((t: any) => ({
|
||
id: t.id,
|
||
name: t.name,
|
||
})),
|
||
attributes: (item.attributes || []).map(attr => ({
|
||
id: attr.id,
|
||
name: attr.name || '',
|
||
position: attr.position,
|
||
visible: attr.visible,
|
||
variation: attr.variation,
|
||
options: attr.options || []
|
||
})),
|
||
variations: mappedVariations,
|
||
permalink: item.permalink,
|
||
raw: item,
|
||
};
|
||
}
|
||
private buildFullAddress(addr: any): string {
|
||
if (!addr) return '';
|
||
const name = addr.fullname || `${addr.first_name || ''} ${addr.last_name || ''}`.trim();
|
||
return [
|
||
name,
|
||
addr.company,
|
||
addr.address_1,
|
||
addr.address_2,
|
||
addr.city,
|
||
addr.state,
|
||
addr.postcode,
|
||
addr.country,
|
||
addr.phone
|
||
].filter(Boolean).join(', ');
|
||
}
|
||
mapOrder(item: WooOrder): UnifiedOrderDTO {
|
||
// 将 WooCommerce 订单数据映射为统一订单DTO
|
||
// 包含账单地址与收货地址以及创建与更新时间
|
||
|
||
// 映射物流追踪信息,将后端格式转换为前端期望的格式
|
||
const fulfillments = (item.fulfillments || []).map((track: any) => ({
|
||
tracking_number: track.tracking_number || '',
|
||
shipping_provider: track.shipping_provider || '',
|
||
shipping_method: track.shipping_method || '',
|
||
status: track.status || '',
|
||
date_created: track.date_created || '',
|
||
items: track.items || [],
|
||
}));
|
||
|
||
return {
|
||
id: item.id,
|
||
number: item.number,
|
||
status: item.status,
|
||
currency: item.currency,
|
||
total: item.total,
|
||
customer_id: item.customer_id,
|
||
customer_email: item.billing?.email || '', // TODO 与 email 重复 保留一个即可
|
||
email: item.billing?.email || '',
|
||
customer_name: `${item.billing?.first_name || ''} ${item.billing?.last_name || ''}`.trim(),
|
||
refunds: item.refunds?.map?.(refund => ({
|
||
id: refund.id,
|
||
reason: refund.reason,
|
||
total: refund.total,
|
||
})),
|
||
line_items: (item.line_items as any[]).map(li => ({
|
||
...li,
|
||
productId: li.product_id,
|
||
})),
|
||
customer_ip_address: item.customer_ip_address ?? '',
|
||
date_paid: item.date_paid ?? '',
|
||
utm_source: item?.meta_data?.find(el => el.key === '_wc_order_attribution_utm_source')?.value || '',
|
||
device_type: item?.meta_data?.find(el => el.key === '_wc_order_attribution_device_type')?.value || '',
|
||
source_type: item?.meta_data?.find(el => el.key === '_wc_order_attribution_source_type')?.value || '',
|
||
billing: item.billing,
|
||
shipping: item.shipping,
|
||
billing_full_address: this.buildFullAddress(item.billing),
|
||
shipping_full_address: this.buildFullAddress(item.shipping),
|
||
payment_method: item.payment_method_title,
|
||
date_created: item.date_created,
|
||
date_modified: item.date_modified,
|
||
shipping_lines: item.shipping_lines,
|
||
fee_lines: item.fee_lines,
|
||
coupon_lines: item.coupon_lines,
|
||
fulfillments,
|
||
raw: item,
|
||
};
|
||
}
|
||
|
||
mapSubscription(item: WooSubscription): UnifiedSubscriptionDTO {
|
||
// 将 WooCommerce 订阅数据映射为统一订阅DTO
|
||
// 若缺少创建时间则回退为开始时间
|
||
return {
|
||
id: item.id,
|
||
status: item.status,
|
||
customer_id: item.customer_id,
|
||
billing_period: item.billing_period,
|
||
billing_interval: item.billing_interval,
|
||
date_created: item.date_created ?? item.start_date,
|
||
date_modified: item.date_modified,
|
||
start_date: item.start_date,
|
||
next_payment_date: item.next_payment_date,
|
||
line_items: item.line_items,
|
||
raw: item,
|
||
};
|
||
}
|
||
|
||
mapMedia(item: WpMedia): UnifiedMediaDTO {
|
||
// 将 WordPress 媒体数据映射为统一媒体DTO
|
||
// 兼容不同字段命名的时间信息
|
||
return {
|
||
id: item.id,
|
||
title:
|
||
typeof item.title === 'string'
|
||
? item.title
|
||
: item.title?.rendered || '',
|
||
media_type: item.media_type,
|
||
mime_type: item.mime_type,
|
||
source_url: item.source_url,
|
||
date_created: item.date_created ?? item.date,
|
||
date_modified: item.date_modified ?? item.modified,
|
||
};
|
||
}
|
||
|
||
async getProducts(
|
||
params: UnifiedSearchParamsDTO
|
||
): Promise<UnifiedPaginationDTO<UnifiedProductDTO>> {
|
||
// 获取产品列表并使用统一分页结构返回
|
||
const requestParams = this.mapProductSearchParams(params);
|
||
const { items, total, totalPages, page, per_page } =
|
||
await this.wpService.fetchResourcePaged<any>(
|
||
this.site,
|
||
'products',
|
||
requestParams
|
||
);
|
||
|
||
// 对于类型为 variable 的产品,需要加载完整的变体数据
|
||
const productsWithVariations = await Promise.all(
|
||
items.map(async (item: any) => {
|
||
// 如果产品类型是 variable 且有变体 ID 列表,则加载完整的变体数据
|
||
if (item.type === 'variable' && item.variations && Array.isArray(item.variations) && item.variations.length > 0) {
|
||
try {
|
||
// 批量获取该产品的所有变体数据
|
||
const variations = await this.wpService.sdkGetAll(
|
||
(this.wpService as any).createApi(this.site, 'wc/v3'),
|
||
`products/${item.id}/variations`
|
||
);
|
||
// 将完整的变体数据添加到产品对象中
|
||
item.variations = variations;
|
||
} catch (error) {
|
||
// 如果获取变体失败,保持原有的 ID 数组
|
||
console.error(`获取产品 ${item.id} 的变体数据失败:`, error);
|
||
}
|
||
}
|
||
return item;
|
||
})
|
||
);
|
||
|
||
return {
|
||
items: productsWithVariations.map(this.mapProduct),
|
||
total,
|
||
totalPages,
|
||
page,
|
||
per_page,
|
||
|
||
};
|
||
}
|
||
|
||
async getAllProducts(params?: UnifiedSearchParamsDTO): Promise<UnifiedProductDTO[]> {
|
||
// 使用sdkGetAll获取所有产品数据,不受分页限制
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const products = await this.wpService.sdkGetAll(api, 'products', params);
|
||
|
||
// 对于类型为 variable 的产品,需要加载完整的变体数据
|
||
const productsWithVariations = await Promise.all(
|
||
products.map(async (product: any) => {
|
||
// 如果产品类型是 variable 且有变体 ID 列表,则加载完整的变体数据
|
||
if (product.type === 'variable' && product.variations && Array.isArray(product.variations) && product.variations.length > 0) {
|
||
try {
|
||
// 批量获取该产品的所有变体数据
|
||
const variations = await this.wpService.sdkGetAll(
|
||
api,
|
||
`products/${product.id}/variations`
|
||
);
|
||
// 将完整的变体数据添加到产品对象中
|
||
product.variations = variations;
|
||
} catch (error) {
|
||
// 如果获取变体失败,保持原有的 ID 数组
|
||
console.error(`获取产品 ${product.id} 的变体数据失败:`, error);
|
||
}
|
||
}
|
||
return product;
|
||
})
|
||
);
|
||
|
||
return productsWithVariations.map((product: any) => this.mapProduct(product));
|
||
}
|
||
|
||
async getProduct(id: string | number): Promise<UnifiedProductDTO> {
|
||
// 获取单个产品详情并映射为统一产品DTO
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.get(`products/${id}`);
|
||
const product = res.data;
|
||
|
||
// 如果产品类型是 variable 且有变体 ID 列表,则加载完整的变体数据
|
||
if (product.type === 'variable' && product.variations && Array.isArray(product.variations) && product.variations.length > 0) {
|
||
try {
|
||
// 批量获取该产品的所有变体数据
|
||
const variations = await this.wpService.sdkGetAll(
|
||
api,
|
||
`products/${product.id}/variations`
|
||
);
|
||
// 将完整的变体数据添加到产品对象中
|
||
product.variations = variations;
|
||
} catch (error) {
|
||
// 如果获取变体失败,保持原有的 ID 数组
|
||
console.error(`获取产品 ${product.id} 的变体数据失败:`, error);
|
||
}
|
||
}
|
||
|
||
return this.mapProduct(product);
|
||
}
|
||
|
||
async createProduct(data: Partial<UnifiedProductDTO>): Promise<UnifiedProductDTO> {
|
||
// 创建产品并返回统一产品DTO
|
||
const res = await this.wpService.createProduct(this.site, data);
|
||
return this.mapProduct(res);
|
||
}
|
||
|
||
async updateProduct(id: string | number, data: Partial<UnifiedProductDTO>): Promise<boolean> {
|
||
// 更新产品并返回统一产品DTO
|
||
const res = await this.wpService.updateProduct(this.site, String(id), data as any);
|
||
return res
|
||
}
|
||
|
||
async getOrderNotes(orderId: string | number): Promise<any[]> {
|
||
// 获取订单备注列表
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.get(`orders/${orderId}/notes`);
|
||
return res.data;
|
||
}
|
||
|
||
async createOrderNote(orderId: string | number, data: any): Promise<any> {
|
||
// 创建订单备注
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.post(`orders/${orderId}/notes`, data);
|
||
return res.data;
|
||
}
|
||
|
||
async deleteProduct(id: string | number): Promise<boolean> {
|
||
// 删除产品
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
try {
|
||
await api.delete(`products/${id}`, { force: true });
|
||
return true;
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
}
|
||
|
||
async batchProcessProducts(
|
||
data: { create?: any[]; update?: any[]; delete?: Array<string | number> }
|
||
): Promise<any> {
|
||
// 批量处理产品增删改
|
||
return await this.wpService.batchProcessProducts(this.site, data);
|
||
}
|
||
|
||
async getOrders(
|
||
params: UnifiedSearchParamsDTO
|
||
): Promise<UnifiedPaginationDTO<UnifiedOrderDTO>> {
|
||
const requestParams = this.mapOrderSearchParams(params);
|
||
const { items, total, totalPages, page, per_page } =
|
||
await this.wpService.fetchResourcePaged<any>(this.site, 'orders', requestParams);
|
||
|
||
// 并行获取所有订单的履行信息
|
||
const ordersWithFulfillments = await Promise.all(
|
||
items.map(async (order: any) => {
|
||
try {
|
||
// 获取订单的履行信息
|
||
const fulfillments = await this.getOrderFulfillments(order.id);
|
||
// 将履行信息添加到订单对象中
|
||
return {
|
||
...order,
|
||
fulfillments: fulfillments || []
|
||
};
|
||
} catch (error) {
|
||
// 如果获取履行信息失败,仍然返回订单,只是履行信息为空数组
|
||
console.error(`获取订单 ${order.id} 的履行信息失败:`, error);
|
||
return {
|
||
...order,
|
||
fulfillments: []
|
||
};
|
||
}
|
||
})
|
||
);
|
||
|
||
return {
|
||
items: ordersWithFulfillments.map(this.mapOrder),
|
||
total,
|
||
totalPages,
|
||
page,
|
||
per_page,
|
||
};
|
||
}
|
||
|
||
async getOrder(id: string | number): Promise<UnifiedOrderDTO> {
|
||
// 获取单个订单详情
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.get(`orders/${id}`);
|
||
return this.mapOrder(res.data);
|
||
}
|
||
|
||
async getAllOrders(params?: UnifiedSearchParamsDTO): Promise<UnifiedOrderDTO[]> {
|
||
// 使用sdkGetAll获取所有订单数据,不受分页限制
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const orders = await this.wpService.sdkGetAll(api, 'orders', params);
|
||
return orders.map((order: any) => this.mapOrder(order));
|
||
}
|
||
|
||
async createOrder(data: Partial<UnifiedOrderDTO>): Promise<UnifiedOrderDTO> {
|
||
// 创建订单并返回统一订单DTO
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.post('orders', data);
|
||
return this.mapOrder(res.data);
|
||
}
|
||
|
||
async updateOrder(id: string | number, data: Partial<UnifiedOrderDTO>): Promise<boolean> {
|
||
// 更新订单并返回布尔结果
|
||
return await this.wpService.updateOrder(this.site, String(id), data as any);
|
||
}
|
||
|
||
async deleteOrder(id: string | number): Promise<boolean> {
|
||
// 删除订单
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
await api.delete(`orders/${id}`, { force: true });
|
||
return true;
|
||
}
|
||
|
||
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> {
|
||
throw new Error('暂无实现')
|
||
// 订单履行(发货)
|
||
// const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
|
||
// try {
|
||
// // 更新订单状态为已完成
|
||
// await api.put(`orders/${orderId}`, { status: 'completed' });
|
||
|
||
// // 如果提供了物流信息,添加到订单备注
|
||
// if (data.tracking_number || data.shipping_provider) {
|
||
// const note = `订单已发货${data.tracking_number ? `,物流单号:${data.tracking_number}` : ''}${data.shipping_provider ? `,物流公司:${data.shipping_provider}` : ''}`;
|
||
// await api.post(`orders/${orderId}/notes`, { note, customer_note: true });
|
||
// }
|
||
|
||
// return {
|
||
// success: true,
|
||
// order_id: orderId,
|
||
// fulfillment_id: `fulfillment_${orderId}_${Date.now()}`,
|
||
// tracking_number: data.tracking_number,
|
||
// shipping_provider: data.shipping_provider,
|
||
// fulfilled_at: new Date().toISOString()
|
||
// };
|
||
// } catch (error) {
|
||
// throw new Error(`履行失败: ${error.message}`);
|
||
// }
|
||
}
|
||
|
||
async cancelFulfillment(orderId: string | number, data: {
|
||
reason?: string;
|
||
shipment_id?: string;
|
||
}): Promise<any> {
|
||
throw new Error('暂未实现')
|
||
// 取消订单履行
|
||
// const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
|
||
// try {
|
||
// // 将订单状态改回处理中
|
||
// await api.put(`orders/${orderId}`, { status: 'processing' });
|
||
|
||
// // 添加取消履行的备注
|
||
// const note = `订单履行已取消${data.reason ? `,原因:${data.reason}` : ''}`;
|
||
// await api.post(`orders/${orderId}/notes`, { 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>> {
|
||
// 获取订阅列表并映射为统一订阅DTO集合
|
||
const { items, total, totalPages, page, per_page } =
|
||
await this.wpService.fetchResourcePaged<any>(
|
||
this.site,
|
||
'subscriptions',
|
||
params
|
||
);
|
||
return {
|
||
items: items.map(this.mapSubscription),
|
||
total,
|
||
totalPages,
|
||
page,
|
||
per_page,
|
||
|
||
};
|
||
}
|
||
|
||
async getAllSubscriptions(params?: UnifiedSearchParamsDTO): Promise<UnifiedSubscriptionDTO[]> {
|
||
// 使用sdkGetAll获取所有订阅数据,不受分页限制
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const subscriptions = await this.wpService.sdkGetAll(api, 'subscriptions', params);
|
||
return subscriptions.map((subscription: any) => this.mapSubscription(subscription));
|
||
}
|
||
|
||
async getMedia(
|
||
params: UnifiedSearchParamsDTO
|
||
): Promise<UnifiedPaginationDTO<UnifiedMediaDTO>> {
|
||
// 获取媒体列表并映射为统一媒体DTO集合
|
||
const { items, total, totalPages, page, per_page } = await this.wpService.fetchMediaPaged(
|
||
this.site,
|
||
params
|
||
);
|
||
return {
|
||
items: items.map(this.mapMedia.bind(this)),
|
||
total,
|
||
totalPages,
|
||
page,
|
||
per_page,
|
||
};
|
||
}
|
||
|
||
async getAllMedia(params?: UnifiedSearchParamsDTO): Promise<UnifiedMediaDTO[]> {
|
||
// 使用sdkGetAll获取所有媒体数据,不受分页限制
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const media = await this.wpService.sdkGetAll(api, 'media', params);
|
||
return media.map((mediaItem: any) => this.mapMedia(mediaItem));
|
||
}
|
||
|
||
mapReview(item: any): UnifiedReviewDTO & { raw: any } {
|
||
// 将 WooCommerce 评论数据映射为统一评论DTO
|
||
return {
|
||
id: item.id,
|
||
product_id: item.product_id,
|
||
author: item.reviewer,
|
||
email: item.reviewer_email,
|
||
content: item.review,
|
||
rating: item.rating,
|
||
status: item.status,
|
||
date_created: item.date_created,
|
||
raw: item
|
||
};
|
||
}
|
||
|
||
async getReviews(
|
||
params: UnifiedSearchParamsDTO
|
||
): Promise<UnifiedReviewPaginationDTO> {
|
||
// 获取评论列表并使用统一分页结构返回
|
||
const requestParams = this.mapProductSearchParams(params);
|
||
const { items, total, totalPages, page, per_page } =
|
||
await this.wpService.fetchResourcePaged<any>(
|
||
this.site,
|
||
'products/reviews',
|
||
requestParams
|
||
);
|
||
return {
|
||
items: items.map(this.mapReview.bind(this)),
|
||
total,
|
||
totalPages,
|
||
page,
|
||
per_page,
|
||
};
|
||
}
|
||
|
||
async getAllReviews(params?: UnifiedSearchParamsDTO): Promise<UnifiedReviewDTO[]> {
|
||
// 使用sdkGetAll获取所有评论数据,不受分页限制
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const reviews = await this.wpService.sdkGetAll(api, 'products/reviews', params);
|
||
return reviews.map((review: any) => this.mapReview(review));
|
||
}
|
||
|
||
async createReview(data: any): Promise<UnifiedReviewDTO> {
|
||
const res = await this.wpService.createReview(this.site, data);
|
||
return this.mapReview(res);
|
||
}
|
||
|
||
async updateReview(id: number, data: any): Promise<UnifiedReviewDTO> {
|
||
const res = await this.wpService.updateReview(this.site, id, data);
|
||
return this.mapReview(res);
|
||
}
|
||
|
||
async deleteReview(id: number): Promise<boolean> {
|
||
return await this.wpService.deleteReview(this.site, id);
|
||
}
|
||
|
||
async deleteMedia(id: string | number): Promise<boolean> {
|
||
// 删除媒体资源
|
||
await this.wpService.deleteMedia(Number(this.site.id), Number(id), true);
|
||
return true;
|
||
}
|
||
|
||
async updateMedia(id: string | number, data: any): Promise<any> {
|
||
// 更新媒体信息
|
||
return await this.wpService.updateMedia(Number(this.site.id), Number(id), data);
|
||
}
|
||
|
||
async convertMediaToWebp(ids: Array<string | number>): Promise<{ converted: any[]; failed: any[] }> {
|
||
// 函数说明 调用服务层将站点的指定媒体批量转换为 webp 并上传
|
||
const result = await this.wpService.convertMediaToWebp(Number(this.site.id), ids);
|
||
return result as any;
|
||
}
|
||
|
||
mapCustomer(item: WooCustomer): UnifiedCustomerDTO {
|
||
// 将 WooCommerce 客户数据映射为统一客户DTO
|
||
// 包含基础信息地址信息与时间信息
|
||
return {
|
||
id: item.id,
|
||
avatar: item.avatar_url,
|
||
email: item.email,
|
||
orders: Number(item.orders ?? 0),
|
||
total_spend: Number(item.total_spent ?? 0),
|
||
first_name: item.first_name,
|
||
last_name: item.last_name,
|
||
username: item.username,
|
||
phone: item.billing?.phone || item.shipping?.phone,
|
||
billing: item.billing,
|
||
shipping: item.shipping,
|
||
date_created: item.date_created,
|
||
date_modified: item.date_modified,
|
||
raw: item,
|
||
};
|
||
}
|
||
async getCustomers(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedCustomerDTO>> {
|
||
const requestParams = this.mapCustomerSearchParams(params);
|
||
const { items, total, totalPages, page, per_page } = await this.wpService.fetchResourcePaged<any>(
|
||
this.site,
|
||
'customers',
|
||
requestParams
|
||
);
|
||
return {
|
||
items: items.map((i: any) => this.mapCustomer(i)),
|
||
total,
|
||
totalPages,
|
||
page,
|
||
per_page,
|
||
|
||
};
|
||
}
|
||
|
||
async getAllCustomers(params?: UnifiedSearchParamsDTO): Promise<UnifiedCustomerDTO[]> {
|
||
// 使用sdkGetAll获取所有客户数据,不受分页限制
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
|
||
// 处理orderBy参数,转换为WooCommerce API需要的格式
|
||
const requestParams = this.mapCustomerSearchParams(params || {});
|
||
|
||
const customers = await this.wpService.sdkGetAll(api, 'customers', requestParams);
|
||
return customers.map((customer: any) => this.mapCustomer(customer));
|
||
}
|
||
|
||
async getCustomer(id: string | number): Promise<UnifiedCustomerDTO> {
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.get(`customers/${id}`);
|
||
return this.mapCustomer(res.data);
|
||
}
|
||
|
||
async createCustomer(data: Partial<UnifiedCustomerDTO>): Promise<UnifiedCustomerDTO> {
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.post('customers', data);
|
||
return this.mapCustomer(res.data);
|
||
}
|
||
|
||
async updateCustomer(id: string | number, data: Partial<UnifiedCustomerDTO>): Promise<UnifiedCustomerDTO> {
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const res = await api.put(`customers/${id}`, data);
|
||
return this.mapCustomer(res.data);
|
||
}
|
||
|
||
async deleteCustomer(id: string | number): Promise<boolean> {
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
await api.delete(`customers/${id}`, { force: true });
|
||
return true;
|
||
}
|
||
|
||
async getOrderFulfillments(orderId: string | number): Promise<any[]> {
|
||
return await this.wpService.getFulfillments(this.site, String(orderId));
|
||
}
|
||
|
||
async createOrderFulfillment(orderId: string | number, data: {
|
||
tracking_number: string;
|
||
shipping_provider: string;
|
||
shipping_method?: string;
|
||
status?: string;
|
||
date_created?: string;
|
||
items?: Array<{
|
||
order_item_id: number;
|
||
quantity: number;
|
||
}>;
|
||
}): Promise<any> {
|
||
const shipmentData: any = {
|
||
shipping_provider: data.shipping_provider,
|
||
tracking_number: data.tracking_number,
|
||
};
|
||
|
||
if (data.shipping_method) {
|
||
shipmentData.shipping_method = data.shipping_method;
|
||
}
|
||
|
||
if (data.status) {
|
||
shipmentData.status = data.status;
|
||
}
|
||
|
||
if (data.date_created) {
|
||
shipmentData.date_created = data.date_created;
|
||
}
|
||
|
||
if (data.items) {
|
||
shipmentData.items = data.items;
|
||
}
|
||
|
||
const response = await this.wpService.createFulfillment(this.site, String(orderId), shipmentData);
|
||
return response.data;
|
||
}
|
||
|
||
async updateOrderFulfillment(orderId: string | number, fulfillmentId: string, data: {
|
||
tracking_number?: string;
|
||
shipping_provider?: string;
|
||
shipping_method?: string;
|
||
status?: string;
|
||
date_created?: string;
|
||
items?: Array<{
|
||
order_item_id: number;
|
||
quantity: number;
|
||
}>;
|
||
}): Promise<any> {
|
||
return await this.wpService.updateFulfillment(this.site, String(orderId), fulfillmentId, data);
|
||
}
|
||
|
||
async deleteOrderFulfillment(orderId: string | number, fulfillmentId: string): Promise<boolean> {
|
||
return await this.wpService.deleteFulfillment(this.site, String(orderId), fulfillmentId);
|
||
}
|
||
|
||
// 映射 WooCommerce 变体到统一格式
|
||
mapVariation(variation: any, productName?: string): UnifiedProductVariationDTO {
|
||
// 将变体属性转换为统一格式
|
||
const mappedAttributes = variation.attributes && Array.isArray(variation.attributes)
|
||
? variation.attributes.map((attr: any) => ({
|
||
id: attr.id,
|
||
name: attr.name || '',
|
||
position: attr.position,
|
||
visible: attr.visible,
|
||
variation: attr.variation,
|
||
option: attr.option || ''
|
||
}))
|
||
: [];
|
||
|
||
// 映射变体图片
|
||
const mappedImage = variation.image
|
||
? {
|
||
id: variation.image.id,
|
||
src: variation.image.src,
|
||
name: variation.image.name,
|
||
alt: variation.image.alt,
|
||
}
|
||
: undefined;
|
||
|
||
return {
|
||
id: variation.id,
|
||
name: variation.name || productName || '',
|
||
sku: variation.sku || '',
|
||
regular_price: String(variation.regular_price || ''),
|
||
sale_price: String(variation.sale_price || ''),
|
||
price: String(variation.price || ''),
|
||
stock_status: variation.stock_status || 'outofstock',
|
||
stock_quantity: variation.stock_quantity || 0,
|
||
attributes: mappedAttributes,
|
||
image: mappedImage,
|
||
description: variation.description || '',
|
||
enabled: variation.status === 'publish',
|
||
downloadable: variation.downloadable || false,
|
||
virtual: variation.virtual || false,
|
||
manage_stock: variation.manage_stock || false,
|
||
weight: variation.weight || '',
|
||
length: variation.dimensions?.length || '',
|
||
width: variation.dimensions?.width || '',
|
||
height: variation.dimensions?.height || '',
|
||
shipping_class: variation.shipping_class || '',
|
||
tax_class: variation.tax_class || '',
|
||
menu_order: variation.menu_order || 0,
|
||
};
|
||
}
|
||
|
||
// 获取产品变体列表
|
||
async getVariations(productId: string | number, params: UnifiedSearchParamsDTO): Promise<UnifiedVariationPaginationDTO> {
|
||
try {
|
||
const page = Number(params.page ?? 1);
|
||
const per_page = Number(params.per_page ?? 20);
|
||
const result = await this.wpService.getVariations(this.site, Number(productId), page, per_page);
|
||
|
||
// 获取产品名称用于变体显示
|
||
const product = await this.wpService.getProduct(this.site, Number(productId));
|
||
const productName = product?.name || '';
|
||
|
||
return {
|
||
items: (result.items as any[]).map((variation: any) => this.mapVariation(variation, productName)),
|
||
total: result.total,
|
||
page: result.page,
|
||
per_page: result.per_page,
|
||
totalPages: result.totalPages,
|
||
};
|
||
} catch (error) {
|
||
throw new Error(`获取产品变体列表失败: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 获取所有产品变体
|
||
async getAllVariations(productId: string | number, params?: UnifiedSearchParamsDTO): Promise<UnifiedProductVariationDTO[]> {
|
||
try {
|
||
const api = (this.wpService as any).createApi(this.site, 'wc/v3');
|
||
const variations = await this.wpService.sdkGetAll(api, `products/${productId}/variations`, params);
|
||
|
||
// 获取产品名称用于变体显示
|
||
const product = await this.wpService.getProduct(this.site, Number(productId));
|
||
const productName = product?.name || '';
|
||
|
||
return variations.map((variation: any) => this.mapVariation(variation, productName));
|
||
} catch (error) {
|
||
throw new Error(`获取所有产品变体失败: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 获取单个产品变体
|
||
async getVariation(productId: string | number, variationId: string | number): Promise<UnifiedProductVariationDTO> {
|
||
try {
|
||
const variation = await this.wpService.getVariation(this.site, Number(productId), Number(variationId));
|
||
|
||
// 获取产品名称用于变体显示
|
||
const product = await this.wpService.getProduct(this.site, Number(productId));
|
||
const productName = product?.name || '';
|
||
|
||
return this.mapVariation(variation, productName);
|
||
} catch (error) {
|
||
throw new Error(`获取产品变体失败: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 创建产品变体
|
||
async createVariation(productId: string | number, data: CreateVariationDTO): Promise<UnifiedProductVariationDTO> {
|
||
try {
|
||
// 将统一DTO转换为WooCommerce API格式
|
||
const createData: any = {
|
||
sku: data.sku,
|
||
regular_price: data.regular_price,
|
||
sale_price: data.sale_price,
|
||
stock_status: data.stock_status,
|
||
stock_quantity: data.stock_quantity,
|
||
description: data.description,
|
||
status: data.enabled ? 'publish' : 'draft',
|
||
downloadable: data.downloadable,
|
||
virtual: data.virtual,
|
||
manage_stock: data.manage_stock,
|
||
weight: data.weight,
|
||
dimensions: {
|
||
length: data.length,
|
||
width: data.width,
|
||
height: data.height,
|
||
},
|
||
shipping_class: data.shipping_class,
|
||
tax_class: data.tax_class,
|
||
menu_order: data.menu_order,
|
||
};
|
||
|
||
// 映射属性
|
||
if (data.attributes && Array.isArray(data.attributes)) {
|
||
createData.attributes = data.attributes.map(attr => ({
|
||
id: attr.id,
|
||
name: attr.name,
|
||
option: attr.option || attr.options?.[0] || '',
|
||
}));
|
||
}
|
||
|
||
// 映射图片
|
||
if (data.image) {
|
||
createData.image = {
|
||
id: data.image.id,
|
||
};
|
||
}
|
||
|
||
const variation = await this.wpService.createVariation(this.site, String(productId), createData);
|
||
|
||
// 获取产品名称用于变体显示
|
||
const product = await this.wpService.getProduct(this.site, Number(productId));
|
||
const productName = product?.name || '';
|
||
|
||
return this.mapVariation(variation, productName);
|
||
} catch (error) {
|
||
throw new Error(`创建产品变体失败: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 更新产品变体
|
||
async updateVariation(productId: string | number, variationId: string | number, data: UpdateVariationDTO): Promise<UnifiedProductVariationDTO> {
|
||
try {
|
||
// 将统一DTO转换为WooCommerce API格式
|
||
const updateData: any = {
|
||
sku: data.sku,
|
||
regular_price: data.regular_price,
|
||
sale_price: data.sale_price,
|
||
stock_status: data.stock_status,
|
||
stock_quantity: data.stock_quantity,
|
||
description: data.description,
|
||
status: data.enabled !== undefined ? (data.enabled ? 'publish' : 'draft') : undefined,
|
||
downloadable: data.downloadable,
|
||
virtual: data.virtual,
|
||
manage_stock: data.manage_stock,
|
||
weight: data.weight,
|
||
shipping_class: data.shipping_class,
|
||
tax_class: data.tax_class,
|
||
menu_order: data.menu_order,
|
||
};
|
||
|
||
// 映射尺寸
|
||
if (data.length || data.width || data.height) {
|
||
updateData.dimensions = {};
|
||
if (data.length) updateData.dimensions.length = data.length;
|
||
if (data.width) updateData.dimensions.width = data.width;
|
||
if (data.height) updateData.dimensions.height = data.height;
|
||
}
|
||
|
||
// 映射属性
|
||
if (data.attributes && Array.isArray(data.attributes)) {
|
||
updateData.attributes = data.attributes.map(attr => ({
|
||
id: attr.id,
|
||
name: attr.name,
|
||
option: attr.option || attr.options?.[0] || '',
|
||
}));
|
||
}
|
||
|
||
// 映射图片
|
||
if (data.image) {
|
||
updateData.image = {
|
||
id: data.image.id,
|
||
};
|
||
}
|
||
|
||
const variation = await this.wpService.updateVariation(this.site, String(productId), String(variationId), updateData);
|
||
|
||
// 获取产品名称用于变体显示
|
||
const product = await this.wpService.getProduct(this.site, Number(productId));
|
||
const productName = product?.name || '';
|
||
|
||
return this.mapVariation(variation, productName);
|
||
} catch (error) {
|
||
throw new Error(`更新产品变体失败: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
|
||
// 删除产品变体
|
||
async deleteVariation(productId: string | number, variationId: string | number): Promise<boolean> {
|
||
try {
|
||
await this.wpService.deleteVariation(this.site, String(productId), String(variationId));
|
||
return true;
|
||
} catch (error) {
|
||
throw new Error(`删除产品变体失败: ${error instanceof Error ? error.message : String(error)}`);
|
||
}
|
||
}
|
||
}
|
||
|