diff --git a/src/adapter/shopyy.adapter.ts b/src/adapter/shopyy.adapter.ts index b4837a0..f0332b9 100644 --- a/src/adapter/shopyy.adapter.ts +++ b/src/adapter/shopyy.adapter.ts @@ -222,6 +222,9 @@ export class ShopyyAdapter implements ISiteAdapter { billing_full_address: formatAddress(billingObj), shipping_full_address: formatAddress(shippingObj), payment_method: item.payment_method, + shipping_lines: item.shipping_lines, + fee_lines: item.fee_lines, + coupon_lines: item.coupon_lines, refunds: [], date_created: typeof item.created_at === 'number' @@ -238,6 +241,8 @@ export class ShopyyAdapter implements ISiteAdapter { }; } + + private mapCustomer(item: ShopyyCustomer): UnifiedCustomerDTO { // 处理多地址结构 const addresses = item.addresses || []; @@ -391,12 +396,12 @@ export class ShopyyAdapter implements ISiteAdapter { } async getAllOrders(params?: UnifiedSearchParamsDTO): Promise { - // Shopyy getAllOrders 暂未实现 - throw new Error('Shopyy getAllOrders 暂未实现'); - } + const data = await this.shopyyService.getAllOrders(this.site.id,params); + return data.map(this.mapOrder.bind(this)); + } async getOrder(id: string | number): Promise { - const data = await this.shopyyService.getOrder(String(this.site.id), String(id)); + const data = await this.shopyyService.getOrder(this.site.id, String(id)); return this.mapOrder(data); } diff --git a/src/adapter/woocommerce.adapter.ts b/src/adapter/woocommerce.adapter.ts index 9c5260b..4416e7a 100644 --- a/src/adapter/woocommerce.adapter.ts +++ b/src/adapter/woocommerce.adapter.ts @@ -417,6 +417,9 @@ export class WooCommerceAdapter implements ISiteAdapter { 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, raw: item, }; } diff --git a/src/dto/shopyy.dto.ts b/src/dto/shopyy.dto.ts index fe63d59..3a7f4ad 100644 --- a/src/dto/shopyy.dto.ts +++ b/src/dto/shopyy.dto.ts @@ -246,6 +246,61 @@ export interface ShopyyOrder { updated_at?: number | string; date_updated?: string; last_modified?: string; + + // 配送方式 + shipping_lines?: Array; + // 费用项 + fee_lines?: Array; + // 优惠券项 + coupon_lines?: Array; +} + + +export class UnifiedShippingLineDTO { + // 配送方式DTO用于承载统一配送方式数据 + id?: string | number; + + method_title?: string; + + method_id?: string; + + total?: string; + + total_tax?: string; + + taxes?: any[]; + + meta_data?: any[]; + +} + +export class UnifiedFeeLineDTO { + // 费用项DTO用于承载统一费用项数据 + id?: string | number; + + name?: string; + + tax_class?: string; + + tax_status?: string; + + total?: string; + + total_tax?: string; + + taxes?: any[]; + + meta_data?: any[]; +} + +export class UnifiedCouponLineDTO { + // 优惠券项DTO用于承载统一优惠券项数据 + id?: string | number; + code?: string; + discount?: string; + discount_tax?: string; + meta_data?: any[]; + } // 客户类型 @@ -324,7 +379,7 @@ export interface ShopyyWebhookEvent { 'event_name': string; 'event_code': string; "event_decript": string; - isemail_event: number; + isemail_event: number; email_event_file: string; email_event_status: number; is_webhook: number; diff --git a/src/dto/site-api.dto.ts b/src/dto/site-api.dto.ts index c3ccf09..8376402 100644 --- a/src/dto/site-api.dto.ts +++ b/src/dto/site-api.dto.ts @@ -303,6 +303,86 @@ export class UnifiedOrderDTO { @ApiProperty({ description: '原始数据', type: 'object', required: false }) raw?: Record; + + @ApiProperty({ description: '配送方式', type: () => [UnifiedShippingLineDTO], required: false }) + shipping_lines?: UnifiedShippingLineDTO[]; + + @ApiProperty({ description: '费用项', type: () => [UnifiedFeeLineDTO], required: false }) + fee_lines?: UnifiedFeeLineDTO[]; + + @ApiProperty({ description: '优惠券项', type: () => [UnifiedCouponLineDTO], required: false }) + coupon_lines?: UnifiedCouponLineDTO[]; +} + +export class UnifiedShippingLineDTO { + // 配送方式DTO用于承载统一配送方式数据 + @ApiProperty({ description: '配送方式ID' }) + id?: string | number; + + @ApiProperty({ description: '配送方式名称' }) + method_title?: string; + + @ApiProperty({ description: '配送方式实例ID' }) + method_id?: string; + + @ApiProperty({ description: '配送方式金额' }) + total?: string; + + @ApiProperty({ description: '配送方式税额' }) + total_tax?: string; + + @ApiProperty({ description: '配送方式税额详情' }) + taxes?: any[]; + + @ApiProperty({ description: '配送方式元数据' }) + meta_data?: any[]; + +} + +export class UnifiedFeeLineDTO { + // 费用项DTO用于承载统一费用项数据 + @ApiProperty({ description: '费用项ID' }) + id?: string | number; + + @ApiProperty({ description: '费用项名称' }) + name?: string; + + @ApiProperty({ description: '税率类' }) + tax_class?: string; + + @ApiProperty({ description: '税率状态' }) + tax_status?: string; + + @ApiProperty({ description: '总金额' }) + total?: string; + + @ApiProperty({ description: '总税额' }) + total_tax?: string; + + @ApiProperty({ description: '税额详情' }) + taxes?: any[]; + + @ApiProperty({ description: '元数据' }) + meta_data?: any[]; +} + +export class UnifiedCouponLineDTO { + // 优惠券项DTO用于承载统一优惠券项数据 + @ApiProperty({ description: '优惠券项ID' }) + id?: string | number; + + @ApiProperty({ description: '优惠券项代码' }) + code?: string; + + @ApiProperty({ description: '优惠券项折扣' }) + discount?: string; + + @ApiProperty({ description: '优惠券项税额' }) + discount_tax?: string; + + @ApiProperty({ description: '优惠券项元数据' }) + meta_data?: any[]; + } export class UnifiedCustomerDTO { diff --git a/src/entity/product.entity.ts b/src/entity/product.entity.ts index d0ec727..ce0cd84 100644 --- a/src/entity/product.entity.ts +++ b/src/entity/product.entity.ts @@ -16,7 +16,7 @@ import { ProductStockComponent } from './product_stock_component.entity'; import { ProductSiteSku } from './product_site_sku.entity'; import { Category } from './category.entity'; -@Entity('product_v2') +@Entity('product') export class Product { @ApiProperty({ example: '1', diff --git a/src/entity/site.entity.ts b/src/entity/site.entity.ts index 9e6db2d..7e03f4b 100644 --- a/src/entity/site.entity.ts +++ b/src/entity/site.entity.ts @@ -2,7 +2,7 @@ import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 't import { Area } from './area.entity'; import { StockPoint } from './stock_point.entity'; -@Entity('site_v2') +@Entity('site') export class Site { @PrimaryGeneratedColumn() id: number; diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 1cde6b4..f5e1503 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -30,6 +30,7 @@ import { ShipmentItem } from '../entity/shipment_item.entity'; import { UpdateStockDTO } from '../dto/stock.dto'; import { StockService } from './stock.service'; import { OrderItemOriginal } from '../entity/order_item_original.entity'; +import { SiteApiService } from './site-api.service'; @Provide() export class OrderService { @@ -91,12 +92,16 @@ export class OrderService { @Inject() siteService: SiteService; + @Inject() + siteApiService: SiteApiService; + async syncOrders(siteId: number, params: Record = {}) { // 调用 WooCommerce API 获取订单 - const orders = await this.wpService.getOrders(siteId, params); + const result = await (await this.siteApiService.getAdapter(siteId)).getAllOrders(params); + let successCount = 0; let failureCount = 0; - for (const order of orders) { + for (const order of result) { try { await this.syncSingleOrder(siteId, order); successCount++; @@ -129,6 +134,13 @@ export class OrderService { [OrderStatus.RETURN_APPROVED]: OrderStatus.ON_HOLD, // 退款申请已通过转为 on-hold [OrderStatus.RETURN_CANCELLED]: OrderStatus.REFUNDED // 已取消退款转为 refunded } + + shopyyOrderAutoNextStatusMap = {//订单状态 100 未完成;110 待处理;180 已完成(确认收货); 190 取消; + [100]: OrderStatus.PENDING, // 100 未完成 转为 pending + [110]: OrderStatus.PROCESSING, // 110 待处理 转为 processing + [180]: OrderStatus.COMPLETED, // 180 已完成(确认收货) 转为 completed + [190]: OrderStatus.CANCEL // 190 取消 转为 cancelled + } // 由于 wordpress 订单状态和 我们的订单状态 不一致,需要做转换 async autoUpdateOrderStatus(siteId: number, order: any) { console.log('更新订单状态', order) @@ -170,6 +182,35 @@ export class OrderService { if (existingOrder) { await this.orderModel.update({ id: existingOrder.id }, { orderStatus: this.mapOrderStatus(order.status) }); } + const site = await this.siteService.get(siteId,true); + if(site.type === 'woocommerce'){ + // 矫正数据库状态 + await this.orderModel.update({ externalOrderId: order.id, siteId: siteId }, { + orderStatus: order.status, + }) + }else if(site.type === 'shopyy'){ + const originStatus = orderData.status; + orderData.status= this.shopyyOrderAutoNextStatusMap[originStatus]; + // 根据currency_code获取对应货币符号 + const currencySymbols: Record = { + 'EUR': '€', + 'USD': '$', + 'GBP': '£', + 'JPY': '¥', + 'AUD': 'A$', + 'CAD': 'C$', + 'CHF': 'CHF', + 'CNY': '¥', + 'HKD': 'HK$', + 'NZD': 'NZ$', + 'SGD': 'S$' + // 可以根据需要添加更多货币代码和符号 + }; + if (orderData.currency) { + const currencyCode = orderData.currency.toUpperCase(); + orderData.currency_symbol = currencySymbols[currencyCode] || '$'; + } + } const externalOrderId = order.id; if ( existingOrder && diff --git a/src/service/shopyy.service.ts b/src/service/shopyy.service.ts index 900ba23..55bfe25 100644 --- a/src/service/shopyy.service.ts +++ b/src/service/shopyy.service.ts @@ -7,7 +7,6 @@ import { Site } from '../entity/site.entity'; import { UnifiedReviewDTO } from '../dto/site-api.dto'; import { ShopyyReview } from '../dto/shopyy.dto'; import { BatchOperationDTO, BatchOperationResultDTO } from '../dto/batch.dto'; - /** * ShopYY平台服务实现 */ @@ -306,14 +305,66 @@ export class ShopyyService { }); return { - items: response.data || [], - total: response.meta?.pagination?.total || 0, - totalPages: response.meta?.pagination?.total_pages || 0, - page: response.meta?.pagination?.current_page || page, - per_page: response.meta?.pagination?.per_page || pageSize + items: response.data?.list || [], + total: response?.data?.paginate?.total || 0, + totalPages: response?.data?.paginate?.pageTotal || 0, + page: response?.data?.paginate?.current || page, + per_page: response?.data?.paginate?.pagesize || pageSize }; } + async getAllOrders(site: any | number, params: Record = {}, maxPages: number = 10, concurrencyLimit: number = 100): Promise { + const firstPage = await this.getOrders(site, 1, 100); + + const { items: firstPageItems, totalPages} = firstPage; + + // const { page = 1, per_page = 100 } = params; + // 如果只有一页数据,直接返回 + if (totalPages <= 1) { + return firstPageItems; + } + + // 限制最大页数,避免过多的并发请求 + const actualMaxPages = Math.min(totalPages, maxPages); + + // 收集所有页面数据,从第二页开始 + const allItems = [...firstPageItems]; + let currentPage = 2; + + // 使用并发限制,避免一次性发起过多请求 + while (currentPage <= actualMaxPages) { + const batchPromises: Promise[] = []; + const batchSize = Math.min(concurrencyLimit, actualMaxPages - currentPage + 1); + + // 创建当前批次的并发请求 + for (let i = 0; i < batchSize; i++) { + const page = currentPage + i; + const pagePromise = this.getOrders(site, page, 100) + .then(pageResult => pageResult.items) + .catch(error => { + console.error(`获取第 ${page} 页数据失败:`, error); + return []; // 如果某页获取失败,返回空数组,不影响整体结果 + }); + + batchPromises.push(pagePromise); + } + + // 等待当前批次完成 + const batchResults = await Promise.all(batchPromises); + + // 合并当前批次的数据 + for (const pageItems of batchResults) { + allItems.push(...pageItems); + } + + // 移动到下一批次 + currentPage += batchSize; + } + + return allItems; + } + + /** * 获取ShopYY订单详情 * @param siteId 站点ID @@ -838,4 +889,5 @@ export class ShopyyService { throw new Error(`删除ShopYY webhook失败: ${errorMessage}`); } } + }