zksu
/
API
forked from yoone/API
1
0
Fork 0

Compare commits

...

3 Commits

Author SHA1 Message Date
tikkhun 99bd7009cc fix: 将origin_id类型从number改为string并重构客户创建逻辑
修改customer.dto.ts中的origin_id类型为string,以保持数据类型一致性
重构customer.service.ts中的客户数据映射逻辑,移除冗余的origin_id转换
在order.service.ts中使用customerService处理客户创建,替代直接操作model
2026-01-05 16:30:48 +08:00
tikkhun 70948ef977 refactor(adapter): 优化ShopYY订单状态映射和查询参数转换
重构ShopYY适配器中的订单状态映射逻辑,将shopyyOrderAutoNextStatusMap重命名为shopyyOrderStatusMap
新增mapUnifiedOrderQueryToShopyyQuery方法处理查询参数转换
移除site-api.controller中多余的where参数处理
2026-01-05 15:36:24 +08:00
tikkhun 22a950d0a0 feat(woocommerce): 添加订单客户IP地址字段
当客户IP地址不存在时,使用空字符串作为默认值
2026-01-04 21:45:58 +08:00
10 changed files with 84 additions and 32 deletions

View File

@ -132,7 +132,7 @@ export class ShopyyAdapter implements ISiteAdapter {
}; };
} }
shopyyOrderAutoNextStatusMap = {//订单状态 100 未完成110 待处理180 已完成(确认收货); 190 取消; shopyyOrderStatusMap = {//订单状态 100 未完成110 待处理180 已完成(确认收货); 190 取消;
[100]: OrderStatus.PENDING, // 100 未完成 转为 pending [100]: OrderStatus.PENDING, // 100 未完成 转为 pending
[110]: OrderStatus.PROCESSING, // 110 待处理 转为 processing [110]: OrderStatus.PROCESSING, // 110 待处理 转为 processing
[180]: OrderStatus.COMPLETED, // 180 已完成(确认收货) 转为 completed [180]: OrderStatus.COMPLETED, // 180 已完成(确认收货) 转为 completed
@ -244,13 +244,13 @@ export class ShopyyAdapter implements ISiteAdapter {
'SGD': 'S$' 'SGD': 'S$'
// 可以根据需要添加更多货币代码和符号 // 可以根据需要添加更多货币代码和符号
}; };
const originStatus = item.status; // 映射订单状态,如果不存在则默认 pending
item.status = this.shopyyOrderAutoNextStatusMap[originStatus]; const status = this.shopyyOrderStatusMap[item.status?? item.order_status] || OrderStatus.PENDING;
return { return {
id: item.id || item.order_id, id: item.id || item.order_id,
number: item.order_number || item.order_sn, number: item.order_number || item.order_sn,
status: String(item.status || item.order_status), status,
currency: item.currency_code || item.currency, currency: item.currency_code || item.currency,
total: String(item.total_price ?? item.total_amount ?? ''), total: String(item.total_price ?? item.total_amount ?? ''),
customer_id: item.customer_id || item.user_id, customer_id: item.customer_id || item.user_id,
@ -427,15 +427,37 @@ export class ShopyyAdapter implements ISiteAdapter {
): Promise<any> { ): Promise<any> {
return await this.shopyyService.batchProcessProducts(this.site, data); return await this.shopyyService.batchProcessProducts(this.site, data);
} }
mapUnifiedOrderQueryToShopyyQuery(params:UnifiedSearchParamsDTO ){
const {where={} as any, ...restParams} = params|| {}
const statusMap = {
'pending': '100', // 100 未完成
'processing': '110', // 110 待处理
'completed': "180", // 180 已完成(确认收货)
'cancelled': '190', // 190 取消
}
const normalizedParams:any= {
...restParams,
}
if(where){
normalizedParams.where = {
...where,
}
if(where.status){
normalizedParams.where.status = statusMap[where.status];
}
}
return normalizedParams
}
async getOrders( async getOrders(
params: UnifiedSearchParamsDTO params: UnifiedSearchParamsDTO
): Promise<UnifiedPaginationDTO<UnifiedOrderDTO>> { ): Promise<UnifiedPaginationDTO<UnifiedOrderDTO>> {
const normalizedParams = this.mapUnifiedOrderQueryToShopyyQuery(params);
const { items, total, totalPages, page, per_page } = const { items, total, totalPages, page, per_page } =
await this.shopyyService.fetchResourcePaged<any>( await this.shopyyService.fetchResourcePaged<any>(
this.site, this.site,
'orders', 'orders',
params normalizedParams
); );
return { return {
items: items.map(this.mapOrder.bind(this)), items: items.map(this.mapOrder.bind(this)),

View File

@ -481,6 +481,7 @@ export class WooCommerceAdapter implements ISiteAdapter {
...li, ...li,
productId: li.product_id, productId: li.product_id,
})), })),
customer_ip_address: item.customer_ip_address ?? '',
date_paid: item.date_paid ?? '', date_paid: item.date_paid ?? '',
utm_source: item?.meta_data?.find(el => el.key === '_wc_order_attribution_utm_source')?.value || '', 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 || '', device_type: item?.meta_data?.find(el => el.key === '_wc_order_attribution_device_type')?.value || '',

View File

@ -663,8 +663,7 @@ export class SiteApiController {
this.logger.info(`[Site API] 获取订单列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`); this.logger.info(`[Site API] 获取订单列表开始, siteId: ${siteId}, query: ${JSON.stringify(query)}`);
try { try {
const adapter = await this.siteApiService.getAdapter(siteId); const adapter = await this.siteApiService.getAdapter(siteId);
const where = { ...(query.where || {}) }; const data = await adapter.getOrders(query);
const data = await adapter.getOrders({ ...query, where });
this.logger.info(`[Site API] 获取订单列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个订单`); this.logger.info(`[Site API] 获取订单列表成功, siteId: ${siteId}, 共获取到 ${data.total} 个订单`);
return successResponse(data); return successResponse(data);
} catch (error) { } catch (error) {

View File

@ -38,12 +38,14 @@ export class WebhookController {
return 'webhook'; return 'webhook';
} }
// TODO 这里得检查一下是否对 SHOPYY有效,否则得另外书写
@Post('/woocommerce') @Post('/woocommerce')
async handleWooWebhook( async handleWooWebhook(
@Body() body: any, @Body() body: any,
@Query('siteId') siteIdStr: string, @Query('siteId') siteIdStr: string,
@Headers() header: any @Headers() header: any
) { ) {
console.log(`webhook woocommerce`, siteIdStr, body,header)
const signature = header['x-wc-webhook-signature']; const signature = header['x-wc-webhook-signature'];
const topic = header['x-wc-webhook-topic']; const topic = header['x-wc-webhook-topic'];
const source = header['x-wc-webhook-source']; const source = header['x-wc-webhook-source'];

View File

@ -73,7 +73,7 @@ export class CreateCustomerDTO {
site_id: number; site_id: number;
@ApiProperty({ description: '原始ID', required: false }) @ApiProperty({ description: '原始ID', required: false })
origin_id?: number; origin_id?: string;
@ApiProperty({ description: '邮箱' }) @ApiProperty({ description: '邮箱' })
email: string; email: string;

View File

@ -2,7 +2,9 @@ import { ApiProperty } from '@midwayjs/swagger';
import { import {
UnifiedPaginationDTO, UnifiedPaginationDTO,
} from './api.dto'; } from './api.dto';
// export class UnifiedOrderWhere{
// []
// }
export class UnifiedTagDTO { export class UnifiedTagDTO {
// 标签DTO用于承载统一标签数据 // 标签DTO用于承载统一标签数据
@ApiProperty({ description: '标签ID' }) @ApiProperty({ description: '标签ID' })

View File

@ -39,7 +39,7 @@ export class CustomerService {
site_id: siteId, // 使用站点ID而不是客户ID site_id: siteId, // 使用站点ID而不是客户ID
site_created_at: this.parseDate(siteCustomer.date_created), site_created_at: this.parseDate(siteCustomer.date_created),
site_updated_at: this.parseDate(siteCustomer.date_modified), site_updated_at: this.parseDate(siteCustomer.date_modified),
origin_id: Number(siteCustomer.id), origin_id: String(siteCustomer.id),
email: siteCustomer.email, email: siteCustomer.email,
first_name: siteCustomer.first_name, first_name: siteCustomer.first_name,
last_name: siteCustomer.last_name, last_name: siteCustomer.last_name,
@ -171,10 +171,7 @@ export class CustomerService {
// 第二步:将站点客户数据转换为客户实体数据 // 第二步:将站点客户数据转换为客户实体数据
const customersData = siteCustomers.map(siteCustomer => { const customersData = siteCustomers.map(siteCustomer => {
return this.mapSiteCustomerToCustomer(siteCustomer, siteId); return this.mapSiteCustomerToCustomer(siteCustomer, siteId);
}).map(customer => ({ })
...customer,
origin_id: String(customer.origin_id),
}));
// 第三步批量upsert客户数据 // 第三步批量upsert客户数据
const upsertResult = await this.upsertManyCustomers(customersData); const upsertResult = await this.upsertManyCustomers(customersData);

View File

@ -1,4 +1,4 @@
import { Inject, Provide } from '@midwayjs/core'; import { Inject, Logger, Provide } from '@midwayjs/core';
import { WPService } from './wp.service'; import { WPService } from './wp.service';
import { Order } from '../entity/order.entity'; import { Order } from '../entity/order.entity';
import { In, Like, Repository } from 'typeorm'; import { In, Like, Repository } from 'typeorm';
@ -37,6 +37,7 @@ 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'; import { UnifiedOrderDTO } from '../dto/site-api.dto';
import { CustomerService } from './customer.service';
@Provide() @Provide()
export class OrderService { export class OrderService {
@ -100,6 +101,12 @@ export class OrderService {
@Inject() @Inject()
siteApiService: SiteApiService; siteApiService: SiteApiService;
@Inject()
customerService: CustomerService;
@Logger()
logger; // 注入 Logger 实例
async syncOrders(siteId: number, params: Record<string, any> = {}): Promise<SyncOperationResult> { async syncOrders(siteId: number, params: Record<string, any> = {}): Promise<SyncOperationResult> {
// 调用 WooCommerce API 获取订单 // 调用 WooCommerce API 获取订单
const result = await (await this.siteApiService.getAdapter(siteId)).getAllOrders(params); const result = await (await this.siteApiService.getAdapter(siteId)).getAllOrders(params);
@ -144,7 +151,7 @@ export class OrderService {
syncResult.processed++; syncResult.processed++;
} }
} }
this.logger.debug('syncOrders result', syncResult)
return syncResult; return syncResult;
} }
@ -202,7 +209,7 @@ export class OrderService {
// 由于 wordpress 订单状态和 我们的订单状态 不一致,需要做转换 // 由于 wordpress 订单状态和 我们的订单状态 不一致,需要做转换
async autoUpdateOrderStatus(siteId: number, order: any) { async autoUpdateOrderStatus(siteId: number, order: any) {
console.log('更新订单状态', order.status, '=>', this.orderAutoNextStatusMap[order.status]) // console.log('更新订单状态', order.status, '=>', this.orderAutoNextStatusMap[order.status])
// 其他状态保持不变 // 其他状态保持不变
const originStatus = order.status; const originStatus = order.status;
// 如果有值就赋值 // 如果有值就赋值
@ -286,7 +293,7 @@ export class OrderService {
externalOrderId, externalOrderId,
coupon_lines, coupon_lines,
}); });
console.log('同步进单个订单2') // console.log('同步进单个订单2')
await this.saveOrderShipping({ await this.saveOrderShipping({
siteId, siteId,
orderId, orderId,
@ -340,16 +347,33 @@ export class OrderService {
return entity; return entity;
} }
entity.orderStatus = this.mapOrderStatus(entity.status); entity.orderStatus = this.mapOrderStatus(entity.status);
const customer = await this.customerModel.findOne({ await this.customerService.upsertCustomer({
where: { email: order.customer_email },
});
if (!customer) {
await this.customerModel.save({
email: order.customer_email, email: order.customer_email,
site_id: siteId, site_id: siteId,
origin_id: String(order.customer_id), origin_id: String(order.customer_id),
billing: order.billing,
shipping: order.shipping,
first_name: order?.billing?.first_name || order?.shipping?.first_name,
last_name: order?.billing?.last_name || order?.shipping?.last_name,
fullname: order?.billing?.fullname || order?.shipping?.fullname,
phone: order?.billing?.phone || order?.shipping?.phone,
// tags:['fromOrder']
}); });
} // const customer = await this.customerModel.findOne({
// where: { email: order.customer_email },
// });
// if (!customer) {
// // 这里用 customer create
// await this.customerModel.save({
// email: order.customer_email,
// site_id: siteId,
// origin_id: String(order.customer_id),
// billing: order.billing,
// shipping: order.shipping,
// phone: order?.billing?.phone || order?.shipping?.phone,
// });
// }
return await this.orderModel.save(entity); return await this.orderModel.save(entity);
} }
@ -394,7 +418,7 @@ export class OrderService {
externalOrderId: string; externalOrderId: string;
orderItems: Record<string, any>[]; orderItems: Record<string, any>[];
}) { }) {
console.log('saveOrderItems params',params) // console.log('saveOrderItems params',params)
const { siteId, orderId, externalOrderId, orderItems } = params; const { siteId, orderId, externalOrderId, orderItems } = params;
const currentOrderItems = await this.orderItemModel.find({ const currentOrderItems = await this.orderItemModel.find({
where: { siteId, externalOrderId: externalOrderId }, where: { siteId, externalOrderId: externalOrderId },

View File

@ -1,4 +1,4 @@
import { Inject, Provide } from '@midwayjs/core'; import { ILogger, Inject, Provide } from '@midwayjs/core';
import axios, { AxiosRequestConfig } from 'axios'; import axios, { AxiosRequestConfig } from 'axios';
import * as fs from 'fs'; import * as fs from 'fs';
import * as FormData from 'form-data'; import * as FormData from 'form-data';
@ -13,6 +13,8 @@ import { UnifiedSearchParamsDTO } from '../dto/api.dto';
*/ */
@Provide() @Provide()
export class ShopyyService { export class ShopyyService {
@Inject()
logger:ILogger;
/** /**
* ShopYY评论列表 * ShopYY评论列表
* @param site * @param site
@ -201,10 +203,12 @@ export class ShopyyService {
page, page,
limit limit
}; };
this.logger.debug('ShopYY API请求分页参数:'+ JSON.stringify(requestParams));
const response = await this.request(site, endpoint, 'GET', null, requestParams); const response = await this.request(site, endpoint, 'GET', null, requestParams);
if (response?.code !== 0) { if (response?.code !== 0) {
throw new Error(response?.msg) throw new Error(response?.msg)
} }
return { return {
items: (response.data.list || []) as T[], items: (response.data.list || []) as T[],
total: response.data?.paginate?.total || 0, total: response.data?.paginate?.total || 0,

View File

@ -47,7 +47,8 @@ export class SiteService {
} }
// 使用 save 方法保存实体及其关联关系 // 使用 save 方法保存实体及其关联关系
await this.siteModel.save(newSite); const result = await this.siteModel.save(newSite);
console.log('result create siteId',result)
return true; return true;
} }