refactor(logistics): 优化物流服务逻辑并移除测试代码

重构物流服务相关代码,移除冗余的测试方法,优化运费计算和订单创建流程
调整freightwaves服务配置,移除调试日志,统一API路径格式
完善物流信息同步逻辑,支持多平台物流服务集成
This commit is contained in:
zhuotianyuan 2026-01-26 16:38:14 +08:00
parent e96ca04f07
commit 3cd36892f6
3 changed files with 120 additions and 370 deletions

View File

@ -34,7 +34,7 @@ export class ShipmentFeeBookDTO {
@ApiProperty()
sender: string;
@ApiProperty()
startPhone: string;
startPhone: string|any;
@ApiProperty()
startPostalCode: string;
@ApiProperty()

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'
};
@ -179,12 +179,6 @@ export class FreightwavesService {
'signature': this.generateSignature(data, date),
};
// 记录请求前的详细信息
console.log(`Sending request to: ${this.config.apiBaseUrl}${url}`, JSON.stringify({
headers,
data
}))
console.log('Request data:', `${this.config.apiBaseUrl}${url}`, data, headers);
// 发送请求 - 临时禁用SSL证书验证以解决UNABLE_TO_VERIFY_LEAF_SIGNATURE错误
const response = await axios.post<FreightwavesResponse<T>>(
`${this.config.apiBaseUrl}${url}`,
@ -267,7 +261,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;
}
@ -314,250 +308,10 @@ export class FreightwavesService {
...params,
partner: this.config.partner,
};
const response = await this.sendRequest<RefundOrderResponseData>('/shipService/order/refundOrder', requestData);
return response.data;
}
/**
*
* 使createOrder方法
*/
async testCreateOrder() {
try {
// 设置必要的配置
this.setConfig({
appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt',
apiBaseUrl: 'http://tms.freightwaves.ca:8901/',
partner: '25072621035200000060'
});
// 准备测试数据
const testParams: Omit<CreateOrderRequest, 'partner'> = {
shipCompany: 'UPSYYZ7000NEW',
partnerOrderNumber: `test-order-${Date.now()}`,
warehouseId: '25072621030107400060',
shipper: {
name: 'John Doe',
phone: '123-456-7890',
company: 'Test Company',
countryCode: 'CA',
city: 'Toronto',
state: 'ON',
address1: '123 Main St',
address2: 'Suite 400',
postCode: 'M5V 2T6',
countryName: 'Canada',
cityName: 'Toronto',
stateName: 'Ontario',
companyName: 'Test Company Inc.'
},
reciver: {
name: 'Jane Smith',
phone: '987-654-3210',
company: 'Receiver Company',
countryCode: 'CA',
city: 'Vancouver',
state: 'BC',
address1: '456 Oak St',
address2: '',
postCode: 'V6J 2A9',
countryName: 'Canada',
cityName: 'Vancouver',
stateName: 'British Columbia',
companyName: 'Receiver Company Ltd.'
},
packages: [
{
dimensions: {
length: 10,
width: 8,
height: 6,
lengthUnit: 'IN',
weight: 5,
weightUnit: 'LB'
},
currency: 'CAD',
description: 'Test Package'
}
],
declaration: {
boxNo: 'BOX-001',
sku: 'TEST-SKU-001',
cnname: '测试产品',
enname: 'Test Product',
declaredPrice: 100,
declaredQty: 1,
material: 'Plastic',
intendedUse: 'General use',
cweight: 5,
hsCode: '39269090',
battery: 'No'
},
signService: 0
};
// 调用创建订单方法
this.log('开始测试创建订单...');
this.log('测试参数:', testParams);
// 注意:在实际环境中取消注释以下行来执行真实请求
const result = await this.createOrder(testParams);
this.log('创建订单成功:', result);
// 返回模拟结果
return {
partnerOrderNumber: testParams.partnerOrderNumber,
shipOrderId: `simulated-shipOrderId-${Date.now()}`
};
} catch (error) {
this.log('测试创建订单失败:', error);
throw error;
}
}
/**
*
* @returns
*/
async testRateTry() {
try {
// 设置必要的配置
this.setConfig({
appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt',
apiBaseUrl: 'http://tms.freightwaves.ca:8901',
partner: '25072621035200000060'
});
// 准备测试数据 - 符合RateTryRequest接口要求
const testParams: Omit<RateTryRequest, 'partner'> = {
shipCompany: 'UPSYYZ7000NEW',
partnerOrderNumber: `test-rate-try-${Date.now()}`,
warehouseId: '25072621030107400060',
shipper: {
name: 'John Doe',
phone: '123-456-7890',
company: 'Test Company',
countryCode: 'CA',
city: 'Toronto',
state: 'ON',
address1: '123 Main St',
address2: 'Suite 400',
postCode: 'M5V 2T6',
countryName: 'Canada',
cityName: 'Toronto',
stateName: 'Ontario',
companyName: 'Test Company Inc.'
},
reciver: {
name: 'Jane Smith',
phone: '987-654-3210',
company: 'Receiver Company',
countryCode: 'CA',
city: 'Vancouver',
state: 'BC',
address1: '456 Oak St',
address2: '',
postCode: 'V6J 2A9',
countryName: 'Canada',
cityName: 'Vancouver',
stateName: 'British Columbia',
companyName: 'Receiver Company Ltd.'
},
packages: [
{
dimensions: {
length: 10,
width: 8,
height: 6,
lengthUnit: 'IN',
weight: 5,
weightUnit: 'LB'
},
currency: 'CAD',
description: 'Test Package'
}
],
signService: 0
};
// 调用费用试算方法
this.log('开始测试费用试算...');
this.log('测试参数:', testParams);
// 注意:在实际环境中取消注释以下行来执行真实请求
const result = await this.rateTry(testParams);
this.log('费用试算成功:', result);
this.log('测试完成:费用试算方法调用成功(模拟)');
this.log('提示在实际环境中取消注释代码中的rateTry调用行来执行真实请求');
// 返回模拟结果
return {
shipCompany: 'DHL',
channelCode: 'DHL-EXPRESS',
totalAmount: 125.50,
currency: 'CAD'
};
} catch (error) {
this.log('测试费用试算失败:', error);
throw error;
}
}
/**
*
* @returns
*/
async testQueryOrder() {
try {
// 设置必要的配置
this.setConfig({
appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt',
apiBaseUrl: 'http://freightwaves.ca:8901',
partner: '25072621035200000060'
});
// 准备测试数据 - 可以通过partnerOrderNumber或shipOrderId查询
const testParams: Omit<QueryOrderRequest, 'partner'> = {
// 选择其中一个参数进行测试
partnerOrderNumber: 'test-order-123456789', // 示例订单号
// shipOrderId: 'simulated-shipOrderId-123456789' // 或者使用运单号
};
// 调用查询订单方法
this.log('开始测试查询订单...');
this.log('测试参数:', testParams);
// 注意:在实际环境中取消注释以下行来执行真实请求
const result = await this.queryOrder(testParams);
this.log('查询订单成功:', result);
this.log('测试完成:查询订单方法调用成功(模拟)');
// 返回模拟结果
return {
thirdOrderId: 'thirdOrder-123456789',
shipCompany: 'DHL',
expressFinish: 0,
expressFailMsg: '',
expressOrder: {
mainTrackingNumber: '1234567890',
labelPath: ['https://example.com/label.pdf'],
totalAmount: 100,
currency: 'CAD',
balance: 50
},
partnerOrderNumber: testParams.partnerOrderNumber,
shipOrderId: 'simulated-shipOrderId-123456789'
};
} catch (error) {
this.log('测试查询订单失败:', error);
throw error;
}
}
/**
* logger可能未定义的情况
* @param message

View File

@ -32,7 +32,6 @@ import { OrderService } from './order.service';
import { convertKeysFromCamelToSnake } from '../utils/object-transform.util';
import { SiteService } from './site.service';
import { FreightwavesService, RateTryRequest } from './freightwaves.service';
@Provide()
export class LogisticsService {
@InjectEntityModel(Service)
serviceModel: Repository<Service>;
@ -318,7 +317,16 @@ export class LogisticsService {
currency: 'CAD',
// item_description: data.sales, // todo: 货品信息
}
const resShipmentFee = await this.uniExpressService.getRates(reqBody);
let resShipmentFee: any;
if (data.shipmentPlatform === 'uniuni') {
resShipmentFee = await this.uniExpressService.getRates(reqBody);
} else if (data.shipmentPlatform === 'freightwaves') {
const fre_reqBody = await this.convertToFreightwavesRateTry(data);
resShipmentFee = await this.freightwavesService.rateTry(fre_reqBody);
} else {
throw new Error('不支持的运单平台');
}
if (resShipmentFee.status !== 'SUCCESS') {
throw new Error(resShipmentFee.ret_msg);
}
@ -343,48 +351,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);
// }
// 记录物流信息,并将订单状态转到完成
// 记录物流信息,并将订单状态转到完成,uniuni状态为SUCCESStms.freightwaves状态为00000200
if (resShipmentOrder.status === 'SUCCESS' || resShipmentOrder.code === '00000200') {
order.orderStatus = ErpOrderStatus.COMPLETED;
} else {
@ -398,6 +367,8 @@ export class LogisticsService {
const shipmentRepo = manager.getRepository(Shipment);
const tracking_provider = data.shipmentPlatform; // todo: id未确定,后写进常数
// 同步物流信息到woocommerce
const site = await this.siteService.get(Number(order.siteId), true);
let co: any;
let unique_id: any;
let state: any;
@ -410,8 +381,9 @@ export class LogisticsService {
unique_id = resShipmentOrder.data?.shipOrderId;
state = ErpOrderStatus.COMPLETED;
}
// 同步物流信息到woocommerce
const site = await this.siteService.get(Number(order.siteId), true);
// 同步订单状态到woocommerce
if (order.source_type != "shopyy") {
const res = await this.wpService.createFulfillment(site, order.externalOrderId, {
tracking_number: co,
tracking_provider: tracking_provider,
@ -431,9 +403,6 @@ export class LogisticsService {
order.shipmentId = shipment.id;
shipmentId = shipment.id;
}
// 同步订单状态到woocommerce
if (order.source_type != "shopyy") {
if (order.status !== OrderStatus.COMPLETED) {
await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.COMPLETED,
@ -441,19 +410,44 @@ 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) {
// // shopyy未提供更新订单接口暂不更新订单状态
// // await this.shopyyService.updateOrder(site, order.externalOrderId, {
// // status: OrderStatus.COMPLETED,
// // });
// order.status = OrderStatus.COMPLETED;
// }
// }
order.orderStatus = ErpOrderStatus.COMPLETED;
await orderRepo.save(order);
}).catch(error => {
transactionError = error
throw new Error(`请求错误:${error}`);
});
if (transactionError !== undefined) {
console.log('err', transactionError);
throw transactionError;
}
// 更新产品发货信息
this.orderService.updateOrderSales(order.id, sales);
@ -726,7 +720,6 @@ export class LogisticsService {
'order_id': order.externalOrderId // todo: 需要获取订单的externalOrderId
}
};
// 添加运单
resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
}
@ -778,16 +771,30 @@ export class LogisticsService {
weightUnit: (data.details.packaging_properties.packages[0].measurements.weight.unit === 'kg' ? 'KG' : 'LB') as 'KG' | 'LB' // 重量单位LB,KG
},
currency: 'CAD', // 币种默认CAD
description: '订单编号:' + order.externalOrderId // 包裹描述(确保是字符串类型)
description: 'site:' + order.siteId + ' orderId:' + order.externalOrderId // 包裹描述(确保是字符串类型)
}
],
signService: 0
// 非跨境订单不需要declaration
// declaration: {
// "boxNo": "", //箱子编号
// "sku": "", //SKU
// "cnname": "", //中文名称
// "enname": "", //英文名称
// "declaredPrice": 1, //申报单价
// "declaredQty": 1, //申报数量
// "material": "", //材质
// "intendedUse": "", //用途
// "cweight": 1, //产品单重
// "hsCode": "", //海关编码
// "battery": "" //电池描述
// }
};
// 调用freightwaves费用试算或创建订单API
// 注意:根据实际需要调用对应的方法
// resShipmentOrder = await this.freightwavesService.rateTry(reqBody); // 费用试算
resShipmentOrder = await this.freightwavesService.createOrder(reqBody); // 创建订单
//tms只返回了物流订单号需要查询一次来获取完整的物流信息
const queryRes = await this.freightwavesService.queryOrder({ shipOrderId: resShipmentOrder.shipOrderId }); // 查询订单
resShipmentOrder.push(queryRes);
}
return resShipmentOrder;
@ -797,46 +804,60 @@ export class LogisticsService {
}
}
/**
* ShipmentFeeBookDTO转换为freightwaves的RateTryRequest格式
* @param data ShipmentFeeBookDTO数据
* @returns RateTryRequest格式的数据
*/
convertToFreightwavesRateTry(data: ShipmentFeeBookDTO): Omit<RateTryRequest, 'partner'> {
/**
* ShipmentFeeBookDTO转换为freightwaves的RateTryRequest格式
* @param data ShipmentFeeBookDTO数据
* @returns RateTryRequest格式的数据
*/
async convertToFreightwavesRateTry(data: ShipmentFeeBookDTO): Promise<Omit<RateTryRequest, 'partner'>> {
const shipments = await this.shippingAddressModel.findOne({
where: {
id: data.address_id,
},
})
const address = shipments?.address;
// 转换为RateTryRequest格式
return {
shipCompany: '', // 必填但ShipmentFeeBookDTO中缺少
const r = {
shipCompany: 'UPSYYZ7000NEW', // 必填但ShipmentFeeBookDTO中缺少
partnerOrderNumber: `order-${Date.now()}`, // 必填,使用时间戳生成
warehouseId: String(data.stockPointId), // 可选使用stockPointId转换
warehouseId: '25072621030107400060', // 可选使用stockPointId转换
shipper: {
name: data.sender, // 必填
phone: data.startPhone, // 必填
company: '', // 必填但ShipmentFeeBookDTO中缺少
phone: data.startPhone.phone, // 必填
company: address.country, // 必填但ShipmentFeeBookDTO中缺少
countryCode: data.shipperCountryCode, // 必填
city: '', // 必填但ShipmentFeeBookDTO中缺少
state: '', // 必填但ShipmentFeeBookDTO中缺少
address1: data.pickupAddress, // 必填
address2: '', // 必填但ShipmentFeeBookDTO中缺少
city: address.city || '', // 必填但ShipmentFeeBookDTO中缺少
state: address.region || '', // 必填但ShipmentFeeBookDTO中缺少
address1: address.address_line_1, // 必填
address2: address.address_line_1 || '', // 必填但ShipmentFeeBookDTO中缺少
postCode: data.startPostalCode, // 必填
countryName: '', // 必填但ShipmentFeeBookDTO中缺少
cityName: '', // 必填但ShipmentFeeBookDTO中缺少
stateName: '', // 必填但ShipmentFeeBookDTO中缺少
companyName: '', // 必填但ShipmentFeeBookDTO中缺少
countryName: address.country || '', // 必填但ShipmentFeeBookDTO中缺少
cityName: address.city || '', // 必填但ShipmentFeeBookDTO中缺少
stateName: address.region || '', // 必填但ShipmentFeeBookDTO中缺少
companyName: address.country || '', // 必填但ShipmentFeeBookDTO中缺少
},
reciver: {
name: data.receiver, // 必填
phone: data.receiverPhone, // 必填
company: '', // 必填但ShipmentFeeBookDTO中缺少
company: address.country,// 必填但ShipmentFeeBookDTO中缺少
countryCode: data.country, // 必填使用country代替countryCode
city: data.city, // 必填
state: data.province, // 必填使用province代替state
address1: data.deliveryAddress, // 必填
address2: '', // 必填但ShipmentFeeBookDTO中缺少
address2: data.deliveryAddress, // 必填但ShipmentFeeBookDTO中缺少
postCode: data.postalCode, // 必填
countryName: '', // 必填但ShipmentFeeBookDTO中缺少
cityName: data.city, // 必填使用city代替cityName
stateName: data.province, // 必填使用province代替stateName
companyName: '', // 必填但ShipmentFeeBookDTO中缺少
countryName: address.country, // 必填但ShipmentFeeBookDTO中缺少
cityName: data.city || '', // 必填使用city代替cityName
stateName: data.province || '', // 必填使用province代替stateName
companyName: address.country || '', // 必填但ShipmentFeeBookDTO中缺少
},
packages: [
{
@ -844,9 +865,9 @@ export class LogisticsService {
length: data.length, // 必填
width: data.width, // 必填
height: data.height, // 必填
lengthUnit: (data.dimensionUom.toUpperCase() === 'CM' ? 'CM' : 'IN') as 'CM' | 'IN', // 必填,转换为有效的单位
lengthUnit: (data.dimensionUom === 'IN' ? 'IN' : 'CM') as 'IN' | 'CM', // 必填,转换为有效的单位
weight: data.weight, // 必填
weightUnit: (data.weightUom.toUpperCase() === 'KG' ? 'KG' : 'LB') as 'KG' | 'LB', // 必填,转换为有效的单位
weightUnit: (data.weightUom === 'LBS' ? 'LB' : 'KG') as 'LB' | 'KG', // 必填,转换为有效的单位
},
currency: 'CAD', // 必填但ShipmentFeeBookDTO中缺少使用默认值
description: 'Package', // 必填但ShipmentFeeBookDTO中缺少使用默认值
@ -854,31 +875,6 @@ export class LogisticsService {
],
signService: 0, // 可选,默认不使用签名服务
};
}
/**
* ShipmentFeeBookDTO缺少的freightwaves必填字段
* @returns
*/
getMissingFreightwavesFields(): string[] {
return [
'shipCompany', // 渠道
'partnerOrderNumber', // 第三方客户订单编号
'shipper.company', // 发货人公司
'shipper.city', // 发货人城市
'shipper.state', // 发货人州/省Code
'shipper.address2', // 发货人详细地址2
'shipper.countryName', // 发货人国家名称
'shipper.cityName', // 发货人城市名称
'shipper.stateName', // 发货人州/省名称
'shipper.companyName', // 发货人公司名称
'reciver.company', // 收货人公司
'reciver.address2', // 收货人详细地址2
'reciver.countryName', // 收货人国家名称
'reciver.companyName', // 收货人公司名称
'packages[0].currency', // 包裹币种
'packages[0].description', // 包裹描述
'partner', // 商户ID
];
return r as any;
}
}