import { ISiteAdapter } from '../interface/site-adapter.interface'; import { UnifiedMediaDTO, UnifiedOrderDTO, UnifiedProductDTO, UnifiedSubscriptionDTO, UnifiedCustomerDTO, UnifiedReviewPaginationDTO, UnifiedReviewDTO, UnifiedWebhookDTO, UnifiedWebhookPaginationDTO, CreateWebhookDTO, UpdateWebhookDTO, CreateVariationDTO, UpdateVariationDTO, UnifiedProductVariationDTO, UnifiedVariationPaginationDTO, CreateReviewDTO, UpdateReviewDTO, } 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'; import { BatchOperationDTO, BatchOperationResultDTO } from '../dto/batch.dto'; export class WooCommerceAdapter implements ISiteAdapter { // 构造函数接收站点配置与服务实例 constructor(private site: Site, private wpService: WPService) { this.mapPlatformToUnifiedProduct = this.mapPlatformToUnifiedProduct.bind(this); this.mapPlatformToUnifiedReview = this.mapPlatformToUnifiedReview.bind(this); this.mapPlatformToUnifiedCustomer = this.mapPlatformToUnifiedCustomer.bind(this); this.mapPlatformToUnifiedMedia = this.mapPlatformToUnifiedMedia.bind(this); this.mapPlatformToUnifiedOrder = this.mapPlatformToUnifiedOrder.bind(this); this.mapPlatformToUnifiedWebhook = this.mapPlatformToUnifiedWebhook.bind(this); } mapUnifiedToPlatformCustomer(data: Partial) { return data } batchProcessProducts?(data: BatchOperationDTO): Promise { throw new Error('Method not implemented.'); } mapCreateVariationParams(data: CreateVariationDTO) { throw new Error('Method not implemented.'); } mapUpdateVariationParams(data: UpdateVariationDTO) { throw new Error('Method not implemented.'); } // ========== 客户映射方法 ========== mapPlatformToUnifiedCustomer(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, }; } mapCustomerSearchParams(params: UnifiedSearchParamsDTO): Record { 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; } // 客户操作方法 async getCustomer(where: Partial>): Promise { const api = this.wpService.createApi(this.site, 'wc/v3'); // 根据提供的条件构建查询参数 let endpoint: string; if (where.id) { endpoint = `customers/${where.id}`; } else if (where.email) { // 使用邮箱查询客户 const res = await api.get('customers', { params: { email: where.email } }); if (!res.data || res.data.length === 0) { throw new Error('Customer not found'); } return this.mapPlatformToUnifiedCustomer(res.data[0]); } else if (where.phone) { // 使用电话查询客户 const res = await api.get('customers', { params: { search: where.phone } }); if (!res.data || res.data.length === 0) { throw new Error('Customer not found'); } return this.mapPlatformToUnifiedCustomer(res.data[0]); } else { throw new Error('Must provide at least one of id, email, or phone'); } const res = await api.get(endpoint); return this.mapPlatformToUnifiedCustomer(res.data); } async getCustomers(params: UnifiedSearchParamsDTO): Promise> { const requestParams = this.mapCustomerSearchParams(params); const { items, total, totalPages, page, per_page } = await this.wpService.fetchResourcePaged( this.site, 'customers', requestParams ); return { items: items.map((i: any) => this.mapPlatformToUnifiedCustomer(i)), total, totalPages, page, per_page, }; } async getAllCustomers(params?: UnifiedSearchParamsDTO): Promise { // 使用sdkGetAll获取所有客户数据,不受分页限制 const api = this.wpService.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.mapPlatformToUnifiedCustomer(customer)); } async createCustomer(data: Partial): Promise { const api = this.wpService.createApi(this.site, 'wc/v3'); const res = await api.post('customers', data); return this.mapPlatformToUnifiedCustomer(res.data); } async updateCustomer(where: Partial>, data: Partial): Promise { const api = this.wpService.createApi(this.site, 'wc/v3'); let customerId: string | number; // 先根据条件获取客户ID if (where.id) { customerId = where.id; } else { // 如果没有提供ID,则先查询客户 const customer = await this.getCustomer(where); customerId = customer.id; } const res = await api.put(`customers/${customerId}`, data); return this.mapPlatformToUnifiedCustomer(res.data); } async deleteCustomer(where: Partial>): Promise { const api = this.wpService.createApi(this.site, 'wc/v3'); let customerId: string | number; // 先根据条件获取客户ID if (where.id) { customerId = where.id; } else { // 如果没有提供ID,则先查询客户 const customer = await this.getCustomer(where); customerId = customer.id; } await api.delete(`customers/${customerId}`, { force: true }); return true; } // ========== 媒体映射方法 ========== mapUnifiedToPlatformMedia(data: Partial) { return data; } mapPlatformToUnifiedMedia(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 getMedia(params: UnifiedSearchParamsDTO): Promise> { // 获取媒体列表并映射为统一媒体DTO集合 const { items, total, totalPages, page, per_page } = await this.wpService.fetchMediaPaged( this.site, params ); return { items: items.map(this.mapPlatformToUnifiedMedia.bind(this)), total, totalPages, page, per_page, }; } async getAllMedia(params?: UnifiedSearchParamsDTO): Promise { // 使用sdkGetAll获取所有媒体数据,不受分页限制 const api = this.wpService.createApi(this.site, 'wc/v3'); const media = await this.wpService.sdkGetAll(api, 'media', params); return media.map((mediaItem: any) => this.mapPlatformToUnifiedMedia(mediaItem)); } createMedia(file: any): Promise { throw new Error('Method not implemented.'); } async updateMedia(where: {id: string | number}, data: any): Promise { // 更新媒体信息 return await this.wpService.updateMedia(Number(this.site.id), Number(where.id), data); } async deleteMedia(where: {id: string | number}): Promise { // 删除媒体资源 await this.wpService.deleteMedia(Number(this.site.id), Number(where.id), true); return true; } async convertMediaToWebp(ids: Array): Promise<{ converted: any[]; failed: any[] }> { // 函数说明 调用服务层将站点的指定媒体批量转换为 webp 并上传 const result = await this.wpService.convertMediaToWebp(Number(this.site.id), ids); return result as any; } // ========== 订单映射方法 ========== mapUnifiedToPlatformOrder(data: Partial) { return data; } mapCreateOrderParams(data: Partial) { return data; } mapUpdateOrderParams(data: Partial) { return data; } mapOrderSearchParams(params: UnifiedSearchParamsDTO): Partial { // 计算分页参数 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 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(', '); } mapPlatformToUnifiedOrder(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, }; } // 订单操作方法 async getOrder(where: {id: string | number}): Promise { // 获取单个订单详情 const api = this.wpService.createApi(this.site, 'wc/v3'); const res = await api.get(`orders/${where.id}`); return this.mapPlatformToUnifiedOrder(res.data); } async getOrders(params: UnifiedSearchParamsDTO): Promise> { const requestParams = this.mapOrderSearchParams(params); const { items, total, totalPages, page, per_page } = await this.wpService.fetchResourcePaged(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.mapPlatformToUnifiedOrder), total, totalPages, page, per_page, }; } async getAllOrders(params?: UnifiedSearchParamsDTO): Promise { // 使用sdkGetAll获取所有订单数据,不受分页限制 const api = this.wpService.createApi(this.site, 'wc/v3'); const orders = await this.wpService.sdkGetAll(api, 'orders', params); return orders.map((order: any) => this.mapPlatformToUnifiedOrder(order)); } async countOrders(where: Record): Promise { // 使用最小分页只获取总数 const searchParams: UnifiedSearchParamsDTO = { where, page: 1, per_page: 1, }; const requestParams = this.mapOrderSearchParams(searchParams); const { total } = await this.wpService.fetchResourcePaged(this.site, 'orders', requestParams); return total || 0; } async createOrder(data: Partial): Promise { // 创建订单并返回统一订单DTO const api = this.wpService.createApi(this.site, 'wc/v3'); const res = await api.post('orders', data); return this.mapPlatformToUnifiedOrder(res.data); } async updateOrder(where: {id: string | number}, data: Partial): Promise { // 更新订单并返回布尔结果 return await this.wpService.updateOrder(this.site, String(where.id), data as any); } async deleteOrder(where: {id: string | number}): Promise { // 删除订单 const api = this.wpService.createApi(this.site, 'wc/v3'); await api.delete(`orders/${where.id}`, { force: true }); return true; } async getOrderFulfillments(orderId: string | number): Promise { 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 { 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 { return await this.wpService.updateFulfillment(this.site, String(orderId), fulfillmentId, data); } async deleteOrderFulfillment(orderId: string | number, fulfillmentId: string): Promise { return await this.wpService.deleteFulfillment(this.site, String(orderId), fulfillmentId); } async getOrderNotes(orderId: string | number): Promise { // 获取订单备注列表 const api = this.wpService.createApi(this.site, 'wc/v3'); const res = await api.get(`orders/${orderId}/notes`); return res.data; } async createOrderNote(orderId: string | number, data: any): Promise { // 创建订单备注 const api = this.wpService.createApi(this.site, 'wc/v3'); const res = await api.post(`orders/${orderId}/notes`, data); return res.data; } async cancelFulfillment(orderId: string | number, data: { reason?: string; shipment_id?: string; }): Promise { throw new Error('暂未实现'); // 取消订单履行 // const api = this.wpService.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}`); // } } // ========== 产品映射方法 ========== mapUnifiedToPlatformProduct(data: Partial): Partial { // 将统一产品DTO映射为WooCommerce产品数据 // 基本字段映射 const mapped: Partial = { id: data.id as number, name: data.name, type: data.type, status: data.status, sku: data.sku, regular_price: data.regular_price, sale_price: data.sale_price, price: data.price, stock_status: data.stock_status as 'instock' | 'outofstock' | 'onbackorder', stock_quantity: data.stock_quantity, // 映射更多WooCommerce产品特有的字段 // featured: data.featured, // catalog_visibility: data.catalog_visibility, // date_on_sale_from: data.date_on_sale_from, // date_on_sale_to: data.date_on_sale_to, // virtual: data.virtual, // downloadable: data.downloadable, // description: data.description, // short_description: data.short_description, // slug: data.slug, // manage_stock: data.manage_stock, // backorders: data.backorders as 'no' | 'notify' | 'yes', // sold_individually: data.sold_individually, // weight: data.weight, // dimensions: data.dimensions, // shipping_class: data.shipping_class, // tax_class: data.tax_class, }; // 映射图片数据 if (data.images && Array.isArray(data.images)) { mapped.images = data.images.map(img => ({ id: img.id as number, src: img.src, name: img.name, alt: img.alt, })); } // 映射分类数据 if (data.categories && Array.isArray(data.categories)) { mapped.categories = data.categories.map(cat => ({ // id: cat.id as number, //TODO name: cat.name, })); } // 映射标签数据 // TODO tags 应该可以设置 // if (data.tags && Array.isArray(data.tags)) { // mapped.tags = data.tags.map(tag => { // return ({ // // id: tag.id as number, // name: tag.name, // }); // }); // } // 映射属性数据 if (data.attributes && Array.isArray(data.attributes)) { mapped.attributes = data.attributes.map(attr => ({ // id 由于我们这个主要用来存,所以不映射 id name: attr.name, visible: attr.visible, variation: attr.variation, options: attr.options })); } // 映射变体数据(注意:WooCommerce API 中变体通常通过单独的端点处理) // 这里只映射变体的基本信息,具体创建/更新变体需要额外处理 if (data.variations && Array.isArray(data.variations)) { // 对于WooProduct类型,variations字段只存储变体ID mapped.variations = data.variations.map(variation => variation.id as number); } // 映射下载数据(如果产品是可下载的) // if (data.downloads && Array.isArray(data.downloads)) { // mapped.downloads = data.downloads.map(download => ({ // id: download.id as number, // name: download.name, // file: download.file, // })); // } return mapped; } mapCreateProductParams(data: Partial):Partial { const {id,...mapped}= this.mapUnifiedToPlatformProduct(data); // 创建不带 id return mapped } mapUpdateProductParams(data: Partial): Partial { return this.mapUnifiedToPlatformProduct(data); } mapProductSearchParams(params: UnifiedSearchParamsDTO): Partial { 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; } mapPlatformToUnifiedProduct(data: WooProduct): UnifiedProductDTO { // 将 WooCommerce 产品数据映射为统一产品DTO // 保留常用字段与时间信息以便前端统一展示 // https://woocommerce.github.io/woocommerce-rest-api-docs/?javascript#product-properties // 映射变体数据 const mappedVariations = data.variations && Array.isArray(data.variations) ? data.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 || data.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: data.id, date_created: data.date_created, date_modified: data.date_modified, type: data.type, // simple grouped external variable status: data.status, // draft pending private publish sku: data.sku, name: data.name, //价格 regular_price: data.regular_price, sale_price: data.sale_price, price: data.price, stock_status: data.stock_status, stock_quantity: data.stock_quantity, images: (data.images || []).map((img: any) => ({ id: img.id, src: img.src, name: img.name, alt: img.alt, })), categories: (data.categories || []).map((c: any) => ({ id: c.id, name: c.name, })), tags: (data.tags || []).map((t: any) => ({ id: t.id, name: t.name, })), attributes: (data.attributes || []).map(attr => ({ id: attr.id, name: attr.name || '', position: attr.position, visible: attr.visible, variation: attr.variation, options: attr.options || [] })), variations: mappedVariations, permalink: data.permalink, raw: data, }; } // 判断是否是这个站点的sku isSiteSkuThisSite(sku: string,){ return sku.startsWith(this.site.skuPrefix+'-'); } async getProduct(where: Partial>): Promise{ const { id, sku } = where; if(id) return this.getProductById(id); if(sku) return this.getProductBySku(sku); throw new Error('必须提供id或sku参数'); } async getProductBySku(sku: string){ // const api = this.wpService.createApi(this.site, 'wc/v3'); // const res = await api.get(`products`,{ // sku // }); // const product = res.data[0]; const res = await this.wpService.getProducts(this.site,{ sku, page:1, per_page:1, }); const product = res?.items?.[0]; if(!product) return null return this.mapPlatformToUnifiedProduct(product); } // 产品操作方法 async getProductById(id: string | number): Promise { // 获取单个产品详情并映射为统一产品DTO const api = this.wpService.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.mapPlatformToUnifiedProduct(product); } async getProducts(params: UnifiedSearchParamsDTO): Promise> { // 获取产品列表并使用统一分页结构返回 const requestParams = this.mapProductSearchParams(params); const { items, total, totalPages, page, per_page } = await this.wpService.fetchResourcePaged( 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.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.mapPlatformToUnifiedProduct), total, totalPages, page, per_page, }; } async getAllProducts(params?: UnifiedSearchParamsDTO): Promise { // 使用sdkGetAll获取所有产品数据,不受分页限制 const api = this.wpService.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.mapPlatformToUnifiedProduct(product)); } async createProduct(data: Partial): Promise { // 创建产品并返回统一产品DTO const createData = this.mapCreateProductParams(data); const res = await this.wpService.createProduct(this.site, createData); return this.mapPlatformToUnifiedProduct(res); } async updateProduct(where: Partial>, data: Partial): Promise { // 更新产品并返回统一产品DTO const product = await this.getProduct(where); if(!product){ throw new Error('产品不存在'); } const updateData = this.mapUpdateProductParams(data); const res = await this.wpService.updateProduct(this.site, String(product.id), updateData as any); return res; } async deleteProduct(where: Partial>): Promise { // 删除产品 const product = await this.getProduct(where); if(!product){ throw new Error('产品不存在'); } const api = this.wpService.createApi(this.site, 'wc/v3'); try { await api.delete(`products/${product.id}`, { force: true }); return true; } catch (e) { return false; } } // ========== 评论映射方法 ========== mapUnifiedToPlatformReview(data: Partial) { return data; } mapCreateReviewParams(data: CreateReviewDTO) { return data; } mapUpdateReviewParams(data: UpdateReviewDTO) { return data; } mapPlatformToUnifiedReview(item: any): UnifiedReviewDTO { // 将 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, date_modified: item.date_modified, }; } // 评论操作方法 async getReviews(params: UnifiedSearchParamsDTO): Promise { // 获取评论列表并使用统一分页结构返回 const requestParams = this.mapProductSearchParams(params); const { items, total, totalPages, page, per_page } = await this.wpService.fetchResourcePaged( this.site, 'products/reviews', requestParams ); return { items: items.map(this.mapPlatformToUnifiedReview.bind(this)), total, totalPages, page, per_page, }; } async getAllReviews(params?: UnifiedSearchParamsDTO): Promise { // 使用sdkGetAll获取所有评论数据,不受分页限制 const api = this.wpService.createApi(this.site, 'wc/v3'); const reviews = await this.wpService.sdkGetAll(api, 'products/reviews', params); return reviews.map((review: any) => this.mapPlatformToUnifiedReview(review)); } async createReview(data: CreateReviewDTO): Promise { const res = await this.wpService.createReview(this.site, data); return this.mapPlatformToUnifiedReview(res); } async updateReview(where: Partial>, data: UpdateReviewDTO): Promise { const { id } = where; if (!id) { throw new Error('必须提供评论ID'); } const res = await this.wpService.updateReview(this.site, Number(id), data); return this.mapPlatformToUnifiedReview(res); } async deleteReview(where: Partial>): Promise { const { id } = where; if (!id) { throw new Error('必须提供评论ID'); } return await this.wpService.deleteReview(this.site, Number(id)); } // ========== 订阅映射方法 ========== mapUnifiedToPlatformSubscription(data: Partial) { return data; } mapPlatformToUnifiedSubscription(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, }; } // 订阅操作方法 async getSubscriptions(params: UnifiedSearchParamsDTO): Promise> { // 获取订阅列表并映射为统一订阅DTO集合 const { items, total, totalPages, page, per_page } = await this.wpService.fetchResourcePaged( this.site, 'subscriptions', params ); return { items: items.map(this.mapPlatformToUnifiedSubscription), total, totalPages, page, per_page, }; } async getAllSubscriptions(params?: UnifiedSearchParamsDTO): Promise { // 使用sdkGetAll获取所有订阅数据,不受分页限制 const api = this.wpService.createApi(this.site, 'wc/v3'); const subscriptions = await this.wpService.sdkGetAll(api, 'subscriptions', params); return subscriptions.map((subscription: any) => this.mapPlatformToUnifiedSubscription(subscription)); } // ========== 变体映射方法 ========== mapPlatformToUnifiedVariation(data: any): UnifiedProductVariationDTO { // 使用mapVariation方法来实现统一的变体映射逻辑 return this.mapVariation(data); } mapUnifiedToPlatformVariation(data: Partial) { return data; } // 映射 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 { 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 { try { const api = this.wpService.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 { 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 { 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 { 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 { 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)}`); } } // ========== 网络钩子映射方法 ========== mapUnifiedToPlatformWebhook(data: Partial) { return data; } mapCreateWebhookParams(data: CreateWebhookDTO) { return data; } mapUpdateWebhookParams(data: UpdateWebhookDTO) { return data; } // 映射 WooCommerce webhook 到统一格式 mapPlatformToUnifiedWebhook(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 { try { const result = await this.wpService.getWebhooks(this.site, params); return { items: (result.items as WooWebhook[]).map(this.mapPlatformToUnifiedWebhook), 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 { try { const api = this.wpService.createApi(this.site, 'wc/v3'); const webhooks = await this.wpService.sdkGetAll(api, 'webhooks', params); return webhooks.map((webhook: any) => this.mapPlatformToUnifiedWebhook(webhook)); } catch (error) { throw new Error(`Failed to get all webhooks: ${error instanceof Error ? error.message : String(error)}`); } } // 获取单个 webhook 详情 async getWebhook(where: {id: string | number}): Promise { try { const result = await this.wpService.getWebhook(this.site, where.id); return this.mapPlatformToUnifiedWebhook(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 { 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.mapPlatformToUnifiedWebhook(result as WooWebhook); } catch (error) { throw new Error(`Failed to create webhook: ${error instanceof Error ? error.message : String(error)}`); } } // 更新现有的 webhook async updateWebhook(where: Partial>, data: UpdateWebhookDTO): Promise { 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, where.id, params); return this.mapPlatformToUnifiedWebhook(result as WooWebhook); } catch (error) { throw new Error(`Failed to update webhook: ${error instanceof Error ? error.message : String(error)}`); } } // 删除指定的 webhook async deleteWebhook(where: Partial>): Promise { try { await this.wpService.deleteWebhook(this.site, where.id); return true; } catch (error) { throw new Error(`Failed to delete webhook: ${error instanceof Error ? error.message : String(error)}`); } } // ========== 其他方法 ========== async getLinks(): Promise> { 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; } batchProcessOrders?(data: BatchOperationDTO): Promise { throw new Error('Method not implemented.'); } batchProcessCustomers?(data: BatchOperationDTO): Promise { throw new Error('Method not implemented.'); } }