main #61

Merged
longbot merged 12 commits from zhuotianyuan/API:main into main 2026-01-23 09:13:11 +00:00
3 changed files with 87 additions and 90 deletions
Showing only changes of commit 2f57dc0d8c - Show all commits

View File

@ -152,7 +152,7 @@ export class FreightwavesService {
// 默认配置
private config: FreightwavesConfig = {
appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt',
apiBaseUrl: 'http://tms.freightwaves.ca:8901/',
apiBaseUrl: 'http://tms.freightwaves.ca:8901',
partner: '25072621035200000060'
};
@ -267,7 +267,7 @@ export class FreightwavesService {
partner: this.config.partner,
};
const response = await this.sendRequest<CreateOrderResponseData>('shipService/order/createOrder', requestData);
const response = await this.sendRequest<CreateOrderResponseData>('/shipService/order/createOrder', requestData);
return response;
}

View File

@ -32,6 +32,7 @@ import { StockPoint } from '../entity/stock_point.entity';
import { OrderService } from './order.service';
import { convertKeysFromCamelToSnake } from '../utils/object-transform.util';
import { SiteService } from './site.service';
import { ShopyyService } from './shopyy.service';
@Provide()
export class LogisticsService {
@ -80,6 +81,9 @@ export class LogisticsService {
@Inject()
wpService: WPService;
@Inject()
shopyyService: ShopyyService;
@Inject()
orderService: OrderService;
@ -358,47 +362,9 @@ export class LogisticsService {
let resShipmentOrder;
try {
//const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId });
// const reqBody = {
// sender: data.details.origin.contact_name,
// start_phone: data.details.origin.phone_number,
// start_postal_code: data.details.origin.address.postal_code.replace(/\s/g, ''),
// pickup_address: data.details.origin.address.address_line_1,
// pickup_warehouse: stock_point.upStreamStockPointId,
// shipper_country_code: data.details.origin.address.country,
// receiver: data.details.destination.contact_name,
// city: data.details.destination.address.city,
// province: data.details.destination.address.region,
// country: data.details.destination.address.country,
// postal_code: data.details.destination.address.postal_code.replace(/\s/g, ''),
// delivery_address: data.details.destination.address.address_line_1,
// receiver_phone: data.details.destination.phone_number.number,
// receiver_email: data.details.destination.email_addresses,
// // item_description: data.sales, // todo: 货品信息
// length: data.details.packaging_properties.packages[0].measurements.cuboid.l,
// width: data.details.packaging_properties.packages[0].measurements.cuboid.w,
// height: data.details.packaging_properties.packages[0].measurements.cuboid.h,
// dimension_uom: data.details.packaging_properties.packages[0].measurements.cuboid.unit,
// weight: data.details.packaging_properties.packages[0].measurements.weight.value,
// weight_uom: data.details.packaging_properties.packages[0].measurements.weight.unit,
// currency: 'CAD',
// custom_field: {
// 'order_id': order.externalOrderId
// }
// }
resShipmentOrder = await this.mepShipment(data, order);
// if (data.shipmentPlatform === 'uniuni') {
// // 添加运单
// resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
// }
// if (data.shipmentPlatform === 'freightwaves') {
// // 添加运单
// resShipmentOrder = await this.freightcomService.createShipment(reqBody);
// }
// 记录物流信息,并将订单状态转到完成
if (resShipmentOrder.status === 'SUCCESS' || resShipmentOrder.code === '00000200') {
order.orderStatus = ErpOrderStatus.COMPLETED;
@ -427,28 +393,28 @@ export class LogisticsService {
unique_id = resShipmentOrder.data?.shipOrderId;
state = ErpOrderStatus.COMPLETED;
}
const res = await this.wpService.createFulfillment(site, order.externalOrderId, {
tracking_number: co,
tracking_provider: tracking_provider,
});
if (order.orderStatus === ErpOrderStatus.COMPLETED) {
const shipment = await shipmentRepo.save({
tracking_provider: tracking_provider,
tracking_id: res.data.tracking_id,
unique_id: unique_id,
stockPointId: String(data.stockPointId), // todo
state: state,
return_tracking_number: co,
fee: data.details.shipmentFee,
order: order
});
order.shipmentId = shipment.id;
shipmentId = shipment.id;
}
// 同步订单状态到woocommerce
if (order.source_type != "shopyy") {
const res = await this.wpService.createFulfillment(site, order.externalOrderId, {
tracking_number: co,
tracking_provider: tracking_provider,
});
if (order.orderStatus === ErpOrderStatus.COMPLETED) {
const shipment = await shipmentRepo.save({
tracking_provider: tracking_provider,
tracking_id: res.data.tracking_id,
unique_id: unique_id,
stockPointId: String(data.stockPointId), // todo
state: state,
return_tracking_number: co,
fee: data.details.shipmentFee,
order: order
});
order.shipmentId = shipment.id;
shipmentId = shipment.id;
}
if (order.status !== OrderStatus.COMPLETED) {
await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.COMPLETED,
@ -456,6 +422,33 @@ export class LogisticsService {
order.status = OrderStatus.COMPLETED;
}
}
if (order.source_type === "shopyy") {
const res = await this.shopyyService.createFulfillment(site, order.externalOrderId, {
tracking_number: co,
tracking_company: resShipmentOrder.shipCompany,
carrier_code: resShipmentOrder.shipperOrderId,
});
if (order.orderStatus === ErpOrderStatus.COMPLETED) {
const shipment = await shipmentRepo.save({
tracking_provider: tracking_provider,
tracking_id: res.data.tracking_id,
unique_id: unique_id,
stockPointId: String(data.stockPointId), // todo
state: state,
return_tracking_number: co,
fee: data.details.shipmentFee,
order: order
});
order.shipmentId = shipment.id;
shipmentId = shipment.id;
}
if (order.status !== OrderStatus.COMPLETED) {
// await this.shopyyService.updateOrder(site, order.externalOrderId, {
// status: OrderStatus.COMPLETED,
// });
order.status = OrderStatus.COMPLETED;
}
}
order.orderStatus = ErpOrderStatus.COMPLETED;
@ -796,7 +789,7 @@ export class LogisticsService {
}
],
signService: 0
// signService: 0, // 签名服务 0不使用, 1使用
// 非跨境订单不需要declaration
// declaration: {
// "boxNo": "", //箱子编号
// "sku": "", //SKU
@ -812,10 +805,11 @@ export class LogisticsService {
// }
};
// 调用freightwaves费用试算或创建订单API
// 注意:根据实际需要调用对应的方法
// resShipmentOrder = await this.freightwavesService.rateTry(reqBody); // 费用试算
resShipmentOrder = await this.freightwavesService.createOrder(reqBody); // 创建订单
const queryRes = await this.freightwavesService.queryOrder({ shipOrderId: resShipmentOrder.shipOrderId }); // 查询订单
console.log('queryRes:', queryRes); // 打印查询结果
resShipmentOrder.push(queryRes);
}
return resShipmentOrder;

View File

@ -10,14 +10,14 @@ import { Site } from '../entity/site.entity';
import { UnifiedReviewDTO } from '../dto/site-api.dto';
import { ShopyyGetOneOrderResult, ShopyyReview } from '../dto/shopyy.dto';
import { BatchOperationDTO, BatchOperationResultDTO } from '../dto/batch.dto';
import { UnifiedSearchParamsDTO,ShopyyGetAllOrdersParams } from '../dto/api.dto';
import { UnifiedSearchParamsDTO, ShopyyGetAllOrdersParams } from '../dto/api.dto';
/**
* ShopYY平台服务实现
*/
@Provide()
export class ShopyyService {
@Inject()
logger:ILogger;
logger: ILogger;
/**
* ShopYY评论列表
* @param site
@ -184,9 +184,9 @@ export class ShopyyService {
*/
public async fetchResourcePaged<T>(site: any, endpoint: string, params: Record<string, any> = {}) {
const response = await this.request(site, endpoint, 'GET', null, params);
return this.mapPageResponse<T>(response,params);
return this.mapPageResponse<T>(response, params);
}
mapPageResponse<T>(response:any,query: Record<string, any>){
mapPageResponse<T>(response: any, query: Record<string, any>) {
if (response?.code !== 0) {
throw new Error(response?.msg)
}
@ -272,7 +272,7 @@ export class ShopyyService {
const response = await this.request(site, `products/${productId}/variations/${variationId}`, 'GET');
return response.data;
}
mapOrderSearchParams(params: UnifiedSearchParamsDTO){
mapOrderSearchParams(params: UnifiedSearchParamsDTO) {
const { after, before, ...restParams } = params;
return {
...restParams,
@ -310,9 +310,9 @@ export class ShopyyService {
async getAllOrders(site: any | number, params: ShopyyGetAllOrdersParams = {}, maxPages: number = 10, concurrencyLimit: number = 100): Promise<any> {
const firstPage = await this.getOrders(site, 1, 100, params);
const { items: firstPageItems, totalPages} = firstPage;
const { items: firstPageItems, totalPages } = firstPage;
// 如果只有一页数据,直接返回
if (totalPages <= 1) {
return firstPageItems;
@ -320,7 +320,7 @@ export class ShopyyService {
// 限制最大页数,避免过多的并发请求
const actualMaxPages = Math.min(totalPages, maxPages);
// 收集所有页面数据,从第二页开始
const allItems = [...firstPageItems];
let currentPage = 2;
@ -329,7 +329,7 @@ export class ShopyyService {
while (currentPage <= actualMaxPages) {
const batchPromises: Promise<any[]>[] = [];
const batchSize = Math.min(concurrencyLimit, actualMaxPages - currentPage + 1);
// 创建当前批次的并发请求
for (let i = 0; i < batchSize; i++) {
const page = currentPage + i;
@ -339,25 +339,25 @@ export class ShopyyService {
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订单详情
@ -475,13 +475,16 @@ export class ShopyyService {
async createFulfillment(site: Site, orderId: string, data: any): Promise<any> {
// ShopYY API: POST /orders/{id}/shipments
const fulfillmentData = {
tracking_number: data.tracking_number,
carrier_code: data.carrier_code,
carrier_name: data.carrier_name,
shipping_method: data.shipping_method
data: [{
order_number: orderId,
tracking_company: data.tracking_company,
tracking_number: data.tracking_number,
carrier_code: data.carrier_code,
note: "note",
mode: ""
}]
};
const response = await this.request(site, `orders/${orderId}/fulfillments`, 'POST', fulfillmentData);
const response = await this.request(site, `orders/fulfillments`, 'POST', fulfillmentData);
return response.data;
}
@ -494,7 +497,7 @@ export class ShopyyService {
*/
async deleteFulfillment(site: any, orderId: string, fulfillmentId: string): Promise<boolean> {
try {
// ShopYY API: DELETE /orders/{order_id}/shipments/{fulfillment_id}
// ShopYY API: DELETE /orders/fulfillments/{fulfillment_id}
await this.request(site, `orders/${orderId}/fulfillments/${fulfillmentId}`, 'DELETE');
return true;
} catch (error) {
@ -542,7 +545,7 @@ export class ShopyyService {
try {
// ShopYY API: PUT /orders/{order_id}/shipments/{tracking_id}
const fulfillmentData: any = {};
// 只传递有值的字段
if (data.tracking_number !== undefined) {
fulfillmentData.tracking_number = data.tracking_number;
@ -645,10 +648,10 @@ export class ShopyyService {
// ShopYY API: POST /products/batch
const response = await this.request(site, 'products/batch', 'POST', data);
const result = response.data;
// 转换 ShopYY 批量操作结果为统一格式
const errors: Array<{identifier: string, error: string}> = [];
const errors: Array<{ identifier: string, error: string }> = [];
// 假设 ShopYY 返回格式与 WooCommerce 类似: { create: [...], update: [...], delete: [...] }
// 错误信息可能在每个项目的 error 字段中
const checkForErrors = (items: any[]) => {
@ -661,12 +664,12 @@ export class ShopyyService {
}
});
};
// 检查每个操作类型的结果中的错误
if (result.create) checkForErrors(result.create);
if (result.update) checkForErrors(result.update);
if (result.delete) checkForErrors(result.delete);
return {
total: (data.create?.length || 0) + (data.update?.length || 0) + (data.delete?.length || 0),
processed: (result.create?.length || 0) + (result.update?.length || 0) + (result.delete?.length || 0),