From 84beb1a65e1ed84e1bd6b7fa5a028056d560a030 Mon Sep 17 00:00:00 2001 From: zhuotianyuan Date: Tue, 30 Dec 2025 11:07:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(order=5Fshipping):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E9=85=8D=E9=80=81=E4=BF=A1=E6=81=AF=E5=AE=9E?= =?UTF-8?q?=E4=BD=93=E5=92=8C=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加 order_shipping.entity.ts 实体类定义 - 更新 shopyy.adapter.ts 支持订单配送数据处理 - 更新 woocommerce.adapter.ts 支持配送信息适配 - 完善 site-adapter.interface.ts 接口定义 - 优化 order.service.ts 配送相关逻辑 - 更新相关 DTO 类以支持配送信息 --- src/adapter/shopyy.adapter.ts | 22 ++++++++++++++++- src/adapter/woocommerce.adapter.ts | 7 ++++++ src/dto/shopyy.dto.ts | 6 +++-- src/dto/site-api.dto.ts | 21 ++++++++++++++-- src/entity/order_shipping.entity.ts | 4 +-- src/interface/site-adapter.interface.ts | 2 +- src/service/order.service.ts | 33 ++++++++++--------------- 7 files changed, 67 insertions(+), 28 deletions(-) diff --git a/src/adapter/shopyy.adapter.ts b/src/adapter/shopyy.adapter.ts index 1d1ece5..528d750 100644 --- a/src/adapter/shopyy.adapter.ts +++ b/src/adapter/shopyy.adapter.ts @@ -17,6 +17,7 @@ import { UnifiedWebhookPaginationDTO, CreateWebhookDTO, UpdateWebhookDTO, + UnifiedShippingLineDTO, } from '../dto/site-api.dto'; import { ShopyyCustomer, @@ -179,12 +180,26 @@ export class ShopyyAdapter implements ISiteAdapter { city: shipping.city || item.shipping_city || '', state: shipping.province || item.shipping_zone || '', postcode: shipping.zip || item.shipping_postcode || '', + method_title: item.payment_method || '', country: shipping.country_name || shipping.country_code || item.shipping_country || '', }; + + // 构建送货地址对象 + const shipping_lines: UnifiedShippingLineDTO[] = [ + { + id: item.fulfillments?.[0]?.id || 0, + method_title: item.payment_method || '', + method_id: item.payment_method || '', + total: item.current_shipping_price.toExponential(2) || '0.00', + total_tax: '0.00', + taxes: [], + meta_data: [], + }, + ]; // 格式化地址为字符串 const formatAddress = (addr: UnifiedAddressDTO) => { @@ -242,6 +257,7 @@ export class ShopyyAdapter implements ISiteAdapter { customer_name: item.customer_name || `${item.firstname} ${item.lastname}`.trim(), email: item.customer_email || item.email, + customer_email: item.customer_email || item.email, line_items: lineItems, sales: lineItems, // 兼容前端 billing: billingObj, @@ -249,9 +265,13 @@ export class ShopyyAdapter implements ISiteAdapter { billing_full_address: formatAddress(billingObj), shipping_full_address: formatAddress(shippingObj), payment_method: item.payment_method, - shipping_lines: item.fulfillments || [], + shipping_lines: shipping_lines || [], fee_lines: item.fee_lines || [], coupon_lines: item.coupon_lines || [], + customer_ip_address: item.ip || '', + device_type: item.source_device || '', + utm_source: item.utm_source || '', + source_type: 'shopyy', date_paid: typeof item.pay_at === 'number' ? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString() : null, diff --git a/src/adapter/woocommerce.adapter.ts b/src/adapter/woocommerce.adapter.ts index 4416e7a..be2a9f6 100644 --- a/src/adapter/woocommerce.adapter.ts +++ b/src/adapter/woocommerce.adapter.ts @@ -420,10 +420,17 @@ export class WooCommerceAdapter implements ISiteAdapter { shipping_lines: item.shipping_lines, fee_lines: item.fee_lines, coupon_lines: item.coupon_lines, + 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 || '', + customer_email: item?.billing?.email || '', + source_type: item?.meta_data?.find(el => el.key === '_wc_order_attribution_source_type')?.value || '', raw: item, }; } + + + private mapSubscription(item: WooSubscription): UnifiedSubscriptionDTO { // 将 WooCommerce 订阅数据映射为统一订阅DTO // 若缺少创建时间则回退为开始时间 diff --git a/src/dto/shopyy.dto.ts b/src/dto/shopyy.dto.ts index f95fba3..a2fba02 100644 --- a/src/dto/shopyy.dto.ts +++ b/src/dto/shopyy.dto.ts @@ -103,7 +103,7 @@ export interface ShopyyOrder { total_amount?: string | number; current_total_price?: string | number; current_subtotal_price?: string | number; - current_shipping_price?: string | number; + current_shipping_price?: number; current_tax_price?: string | number; current_coupon_price?: string | number; current_payment_price?: string | number; @@ -247,7 +247,9 @@ export interface ShopyyOrder { date_updated?: string; last_modified?: string; // 支付时间 - pay_at?: number ; + pay_at?: number | null; + ip?: string; + utm_source?: string; // 配送方式 shipping_lines?: Array; // 费用项 diff --git a/src/dto/site-api.dto.ts b/src/dto/site-api.dto.ts index 6d6c167..ad8fcd2 100644 --- a/src/dto/site-api.dto.ts +++ b/src/dto/site-api.dto.ts @@ -91,6 +91,9 @@ export class UnifiedAddressDTO { @ApiProperty({ description: '电话', required: false }) phone?: string; + + @ApiProperty({ description: '配送方式', required: false }) + method_title?: string; } export class UnifiedOrderLineItemDTO { @@ -279,6 +282,9 @@ export class UnifiedOrderDTO { @ApiProperty({ description: '客户邮箱' }) email: string; + @ApiProperty({ description: '客户邮箱' }) + customer_email: string; + @ApiProperty({ description: '订单项(具体的商品)', type: () => [UnifiedOrderLineItemDTO] }) line_items: UnifiedOrderLineItemDTO[]; @@ -323,7 +329,19 @@ export class UnifiedOrderDTO { coupon_lines?: UnifiedCouponLineDTO[]; @ApiProperty({ description: '支付时间', required: false }) - date_paid?: string ; + date_paid?: string | null; + + @ApiProperty({ description: '客户IP地址', required: false }) + customer_ip_address?: string; + + @ApiProperty({ description: 'UTM来源', required: false }) + utm_source?: string; + + @ApiProperty({ description: '设备类型', required: false }) + device_type?: string; + + @ApiProperty({ description: '来源类型', required: false }) + source_type?: string; } export class UnifiedShippingLineDTO { @@ -350,7 +368,6 @@ export class UnifiedShippingLineDTO { meta_data?: any[]; } - export class UnifiedFeeLineDTO { // 费用项DTO用于承载统一费用项数据 @ApiProperty({ description: '费用项ID' }) diff --git a/src/entity/order_shipping.entity.ts b/src/entity/order_shipping.entity.ts index 8220992..4bc3167 100644 --- a/src/entity/order_shipping.entity.ts +++ b/src/entity/order_shipping.entity.ts @@ -47,9 +47,9 @@ export class OrderShipping { method_id: string; @ApiProperty() - @Column() + @Column({ nullable: true }) @Expose() - instance_id: string; + instance_id?: string; @ApiProperty() @Column('decimal', { precision: 10, scale: 2 }) diff --git a/src/interface/site-adapter.interface.ts b/src/interface/site-adapter.interface.ts index b480ea6..a3b5fb4 100644 --- a/src/interface/site-adapter.interface.ts +++ b/src/interface/site-adapter.interface.ts @@ -45,7 +45,7 @@ export interface ISiteAdapter { /** * 获取单个订单 */ - getOrder(id: string | number): Promise; + getOrder(id: string | number): Promise; /** * 获取订阅列表 diff --git a/src/service/order.service.ts b/src/service/order.service.ts index 01a22a3..5c51bc9 100644 --- a/src/service/order.service.ts +++ b/src/service/order.service.ts @@ -34,6 +34,7 @@ import { SiteApiService } from './site-api.service'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; +import { UnifiedOrderDTO } from '../dto/site-api.dto'; @Provide() export class OrderService { @@ -137,7 +138,10 @@ export class OrderService { async syncOrderById(siteId: number, orderId: string) { try { // 调用 WooCommerce API 获取订单 - const order = await this.wpService.getOrder(siteId, orderId); + //const order = await this.wpService.getOrder(siteId, orderId); + const order = await (await this.siteApiService.getAdapter(siteId)).getOrder( + orderId, + ); await this.syncSingleOrder(siteId, order, true); return { success: true, message: '同步成功' }; } catch (error) { @@ -220,6 +224,7 @@ export class OrderService { externalOrderId, orderItems: line_items, }); + console.log('同步进单个订单1') await this.saveOrderRefunds({ siteId, orderId, @@ -238,6 +243,7 @@ export class OrderService { externalOrderId, coupon_lines, }); + console.log('同步进单个订单2') await this.saveOrderShipping({ siteId, orderId, @@ -273,27 +279,14 @@ export class OrderService { } } - async saveOrder(siteId: number, order: Order): Promise { - order.externalOrderId = String(order.id); - order.siteId = siteId; - delete order.id; - order.device_type = - order?.meta_data?.find( - el => el.key === '_wc_order_attribution_device_type' - )?.value || ''; - order.source_type = - order?.meta_data?.find( - el => el.key === '_wc_order_attribution_source_type' - )?.value || ''; - order.utm_source = - order?.meta_data?.find( - el => el.key === '_wc_order_attribution_utm_source' - )?.value || ''; - order.customer_email = order?.billing?.email || order?.shipping?.email; + async saveOrder(siteId: number, order: UnifiedOrderDTO): Promise { + const externalOrderId = String(order.id) + delete order.id + // order.billing_phone = order?.billing?.phone || order?.shipping?.phone; - const entity = plainToClass(Order, order); + const entity = plainToClass(Order, {...order, externalOrderId, siteId}); const existingOrder = await this.orderModel.findOne({ - where: { externalOrderId: order.externalOrderId, siteId: siteId }, + where: { externalOrderId, siteId: siteId }, }); if (existingOrder) { if (this.canUpdateErpStatus(existingOrder.orderStatus)) { -- 2.40.1