797 lines
30 KiB
TypeScript
797 lines
30 KiB
TypeScript
import { ISiteAdapter } from '../interface/site-adapter.interface';
|
|
import { IPlatformService } from '../interface/platform.interface';
|
|
import {
|
|
UnifiedMediaDTO,
|
|
UnifiedOrderDTO,
|
|
UnifiedPaginationDTO,
|
|
UnifiedProductDTO,
|
|
UnifiedSearchParamsDTO,
|
|
UnifiedSubscriptionDTO,
|
|
UnifiedCustomerDTO,
|
|
UnifiedReviewPaginationDTO,
|
|
UnifiedReviewDTO,
|
|
UnifiedWebhookDTO,
|
|
UnifiedWebhookPaginationDTO,
|
|
CreateWebhookDTO,
|
|
UpdateWebhookDTO,
|
|
} from '../dto/site-api.dto';
|
|
import {
|
|
WooProduct,
|
|
WooOrder,
|
|
WooSubscription,
|
|
WpMedia,
|
|
WooCustomer,
|
|
WooWebhook,
|
|
WooOrderSearchParams,
|
|
WooProductSearchParams,
|
|
} from '../dto/woocommerce.dto';
|
|
import { Site } from '../entity/site.entity';
|
|
|
|
export class WooCommerceAdapter implements ISiteAdapter {
|
|
// 构造函数接收站点配置与服务实例
|
|
constructor(private site: Site, private wpService: IPlatformService) {
|
|
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 到统一格式
|
|
private 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)}`);
|
|
}
|
|
}
|
|
|
|
// 获取单个 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.');
|
|
}
|
|
|
|
|
|
|
|
private 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;
|
|
}
|
|
|
|
private 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;
|
|
}
|
|
|
|
private 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,
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
private mapProduct(item: WooProduct): UnifiedProductDTO {
|
|
// 将 WooCommerce 产品数据映射为统一产品DTO
|
|
// 保留常用字段与时间信息以便前端统一展示
|
|
// https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#product-properties
|
|
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: item.variations as any,
|
|
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(', ');
|
|
}
|
|
private mapOrder(item: WooOrder): UnifiedOrderDTO {
|
|
// 将 WooCommerce 订单数据映射为统一订单DTO
|
|
// 包含账单地址与收货地址以及创建与更新时间
|
|
return {
|
|
id: item.id,
|
|
number: item.number,
|
|
status: item.status,
|
|
currency: item.currency,
|
|
total: item.total,
|
|
customer_id: item.customer_id,
|
|
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,
|
|
})),
|
|
email: item.billing?.email || '',
|
|
line_items: (item.line_items as any[]).map(li => ({
|
|
...li,
|
|
productId: li.product_id,
|
|
})),
|
|
|
|
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,
|
|
raw: item,
|
|
};
|
|
}
|
|
|
|
private 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,
|
|
};
|
|
}
|
|
|
|
private 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
|
|
);
|
|
return {
|
|
items: items.map(this.mapProduct),
|
|
total,
|
|
totalPages,
|
|
page,
|
|
per_page,
|
|
|
|
};
|
|
}
|
|
|
|
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}`);
|
|
return this.mapProduct(res.data);
|
|
}
|
|
|
|
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 updateVariation(productId: string | number, variationId: string | number, data: any): Promise<any> {
|
|
// 更新变体信息并返回结果
|
|
const res = await this.wpService.updateVariation(this.site, String(productId), String(variationId), data);
|
|
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);
|
|
return {
|
|
items: items.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 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 shipOrder(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,
|
|
// 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> {
|
|
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 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,
|
|
};
|
|
}
|
|
|
|
private 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 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;
|
|
}
|
|
|
|
private 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 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;
|
|
}
|
|
}
|
|
|