feat(order_shipping): 添加订单配送信息实体和相关接口

- 添加 order_shipping.entity.ts 实体类定义
- 更新 shopyy.adapter.ts 支持订单配送数据处理
- 更新 woocommerce.adapter.ts 支持配送信息适配
- 完善 site-adapter.interface.ts 接口定义
- 优化 order.service.ts 配送相关逻辑
- 更新相关 DTO 类以支持配送信息
This commit is contained in:
zhuotianyuan 2025-12-30 11:07:37 +08:00
parent 2f99e27f0f
commit 84beb1a65e
7 changed files with 67 additions and 28 deletions

View File

@ -17,6 +17,7 @@ import {
UnifiedWebhookPaginationDTO, UnifiedWebhookPaginationDTO,
CreateWebhookDTO, CreateWebhookDTO,
UpdateWebhookDTO, UpdateWebhookDTO,
UnifiedShippingLineDTO,
} from '../dto/site-api.dto'; } from '../dto/site-api.dto';
import { import {
ShopyyCustomer, ShopyyCustomer,
@ -179,6 +180,7 @@ export class ShopyyAdapter implements ISiteAdapter {
city: shipping.city || item.shipping_city || '', city: shipping.city || item.shipping_city || '',
state: shipping.province || item.shipping_zone || '', state: shipping.province || item.shipping_zone || '',
postcode: shipping.zip || item.shipping_postcode || '', postcode: shipping.zip || item.shipping_postcode || '',
method_title: item.payment_method || '',
country: country:
shipping.country_name || shipping.country_name ||
shipping.country_code || shipping.country_code ||
@ -186,6 +188,19 @@ export class ShopyyAdapter implements ISiteAdapter {
'', '',
}; };
// 构建送货地址对象
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) => { const formatAddress = (addr: UnifiedAddressDTO) => {
return [ return [
@ -242,6 +257,7 @@ export class ShopyyAdapter implements ISiteAdapter {
customer_name: customer_name:
item.customer_name || `${item.firstname} ${item.lastname}`.trim(), item.customer_name || `${item.firstname} ${item.lastname}`.trim(),
email: item.customer_email || item.email, email: item.customer_email || item.email,
customer_email: item.customer_email || item.email,
line_items: lineItems, line_items: lineItems,
sales: lineItems, // 兼容前端 sales: lineItems, // 兼容前端
billing: billingObj, billing: billingObj,
@ -249,9 +265,13 @@ export class ShopyyAdapter implements ISiteAdapter {
billing_full_address: formatAddress(billingObj), billing_full_address: formatAddress(billingObj),
shipping_full_address: formatAddress(shippingObj), shipping_full_address: formatAddress(shippingObj),
payment_method: item.payment_method, payment_method: item.payment_method,
shipping_lines: item.fulfillments || [], shipping_lines: shipping_lines || [],
fee_lines: item.fee_lines || [], fee_lines: item.fee_lines || [],
coupon_lines: item.coupon_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' date_paid: typeof item.pay_at === 'number'
? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString() ? item.pay_at === 0 ? null : new Date(item.pay_at * 1000).toISOString()
: null, : null,

View File

@ -420,10 +420,17 @@ export class WooCommerceAdapter implements ISiteAdapter {
shipping_lines: item.shipping_lines, shipping_lines: item.shipping_lines,
fee_lines: item.fee_lines, fee_lines: item.fee_lines,
coupon_lines: item.coupon_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, raw: item,
}; };
} }
private mapSubscription(item: WooSubscription): UnifiedSubscriptionDTO { private mapSubscription(item: WooSubscription): UnifiedSubscriptionDTO {
// 将 WooCommerce 订阅数据映射为统一订阅DTO // 将 WooCommerce 订阅数据映射为统一订阅DTO
// 若缺少创建时间则回退为开始时间 // 若缺少创建时间则回退为开始时间

View File

@ -103,7 +103,7 @@ export interface ShopyyOrder {
total_amount?: string | number; total_amount?: string | number;
current_total_price?: string | number; current_total_price?: string | number;
current_subtotal_price?: string | number; current_subtotal_price?: string | number;
current_shipping_price?: string | number; current_shipping_price?: number;
current_tax_price?: string | number; current_tax_price?: string | number;
current_coupon_price?: string | number; current_coupon_price?: string | number;
current_payment_price?: string | number; current_payment_price?: string | number;
@ -247,7 +247,9 @@ export interface ShopyyOrder {
date_updated?: string; date_updated?: string;
last_modified?: string; last_modified?: string;
// 支付时间 // 支付时间
pay_at?: number ; pay_at?: number | null;
ip?: string;
utm_source?: string;
// 配送方式 // 配送方式
shipping_lines?: Array<ShopyyShippingLineDTO>; shipping_lines?: Array<ShopyyShippingLineDTO>;
// 费用项 // 费用项

View File

@ -91,6 +91,9 @@ export class UnifiedAddressDTO {
@ApiProperty({ description: '电话', required: false }) @ApiProperty({ description: '电话', required: false })
phone?: string; phone?: string;
@ApiProperty({ description: '配送方式', required: false })
method_title?: string;
} }
export class UnifiedOrderLineItemDTO { export class UnifiedOrderLineItemDTO {
@ -279,6 +282,9 @@ export class UnifiedOrderDTO {
@ApiProperty({ description: '客户邮箱' }) @ApiProperty({ description: '客户邮箱' })
email: string; email: string;
@ApiProperty({ description: '客户邮箱' })
customer_email: string;
@ApiProperty({ description: '订单项(具体的商品)', type: () => [UnifiedOrderLineItemDTO] }) @ApiProperty({ description: '订单项(具体的商品)', type: () => [UnifiedOrderLineItemDTO] })
line_items: UnifiedOrderLineItemDTO[]; line_items: UnifiedOrderLineItemDTO[];
@ -323,7 +329,19 @@ export class UnifiedOrderDTO {
coupon_lines?: UnifiedCouponLineDTO[]; coupon_lines?: UnifiedCouponLineDTO[];
@ApiProperty({ description: '支付时间', required: false }) @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 { export class UnifiedShippingLineDTO {
@ -350,7 +368,6 @@ export class UnifiedShippingLineDTO {
meta_data?: any[]; meta_data?: any[];
} }
export class UnifiedFeeLineDTO { export class UnifiedFeeLineDTO {
// 费用项DTO用于承载统一费用项数据 // 费用项DTO用于承载统一费用项数据
@ApiProperty({ description: '费用项ID' }) @ApiProperty({ description: '费用项ID' })

View File

@ -47,9 +47,9 @@ export class OrderShipping {
method_id: string; method_id: string;
@ApiProperty() @ApiProperty()
@Column() @Column({ nullable: true })
@Expose() @Expose()
instance_id: string; instance_id?: string;
@ApiProperty() @ApiProperty()
@Column('decimal', { precision: 10, scale: 2 }) @Column('decimal', { precision: 10, scale: 2 })

View File

@ -45,7 +45,7 @@ export interface ISiteAdapter {
/** /**
* *
*/ */
getOrder(id: string | number): Promise<UnifiedOrderDTO>; getOrder(id: string | number): Promise<UnifiedOrderDTO>;
/** /**
* *

View File

@ -34,6 +34,7 @@ import { SiteApiService } from './site-api.service';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import * as os from 'os'; import * as os from 'os';
import { UnifiedOrderDTO } from '../dto/site-api.dto';
@Provide() @Provide()
export class OrderService { export class OrderService {
@ -137,7 +138,10 @@ export class OrderService {
async syncOrderById(siteId: number, orderId: string) { async syncOrderById(siteId: number, orderId: string) {
try { try {
// 调用 WooCommerce API 获取订单 // 调用 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); await this.syncSingleOrder(siteId, order, true);
return { success: true, message: '同步成功' }; return { success: true, message: '同步成功' };
} catch (error) { } catch (error) {
@ -220,6 +224,7 @@ export class OrderService {
externalOrderId, externalOrderId,
orderItems: line_items, orderItems: line_items,
}); });
console.log('同步进单个订单1')
await this.saveOrderRefunds({ await this.saveOrderRefunds({
siteId, siteId,
orderId, orderId,
@ -238,6 +243,7 @@ export class OrderService {
externalOrderId, externalOrderId,
coupon_lines, coupon_lines,
}); });
console.log('同步进单个订单2')
await this.saveOrderShipping({ await this.saveOrderShipping({
siteId, siteId,
orderId, orderId,
@ -273,27 +279,14 @@ export class OrderService {
} }
} }
async saveOrder(siteId: number, order: Order): Promise<Order> { async saveOrder(siteId: number, order: UnifiedOrderDTO): Promise<Order> {
order.externalOrderId = String(order.id); const externalOrderId = String(order.id)
order.siteId = siteId; delete order.id
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;
// order.billing_phone = order?.billing?.phone || order?.shipping?.phone; // 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({ const existingOrder = await this.orderModel.findOne({
where: { externalOrderId: order.externalOrderId, siteId: siteId }, where: { externalOrderId, siteId: siteId },
}); });
if (existingOrder) { if (existingOrder) {
if (this.canUpdateErpStatus(existingOrder.orderStatus)) { if (this.canUpdateErpStatus(existingOrder.orderStatus)) {