import { Inject, Provide } from '@midwayjs/core'; import axios from 'axios'; import * as crypto from 'crypto'; import dayjs = require('dayjs'); import utc = require('dayjs/plugin/utc'); import timezone = require('dayjs/plugin/timezone'); // 扩展dayjs功能 dayjs.extend(utc); dayjs.extend(timezone); // 全局参数配置接口 interface FreightwavesConfig { appSecret: string; apiBaseUrl: string; partner: string; } // 地址信息接口 interface Address { name: string; phone: string; company: string; countryCode: string; city: string; state: string; address1: string; address2: string; postCode: string; zoneCode?: string; countryName: string; cityName: string; stateName: string; companyName: string; } // 包裹尺寸接口 interface Dimensions { length: number; width: number; height: number; lengthUnit: 'IN' | 'CM'; weight: number; weightUnit: 'LB' | 'KG'; } // 包裹信息接口 interface Package { dimensions: Dimensions; currency: string; description: string; } // 申报信息接口 interface Declaration { boxNo: string; sku: string; cnname: string; enname: string; declaredPrice: number; declaredQty: number; material: string; intendedUse: string; cweight: number; hsCode: string; battery: string; } // 费用试算请求接口 interface RateTryRequest { shipCompany: string; partnerOrderNumber: string; warehouseId?: string; shipper: Address; reciver: Address; packages: Package[]; partner: string; signService?: 0 | 1; } // 创建订单请求接口 interface CreateOrderRequest extends RateTryRequest { declaration: Declaration; } // 查询订单请求接口 interface QueryOrderRequest { partnerOrderNumber?: string; shipOrderId?: string; partner: string; } // 修改订单请求接口 interface ModifyOrderRequest extends CreateOrderRequest { shipOrderId: string; } // 订单退款请求接口 interface RefundOrderRequest { shipOrderId: string; partner: string; } // 通用响应接口 interface FreightwavesResponse { code: string; msg: string; data: T; } // 费用试算响应数据接口 interface RateTryResponseData { shipCompany: string; channelCode: string; totalAmount: number; currency: string; } // 创建订单响应数据接口 interface CreateOrderResponseData { partnerOrderNumber: string; shipOrderId: string; } // 查询订单响应数据接口 interface QueryOrderResponseData { thirdOrderId: string; shipCompany: string; expressFinish: 0 | 1 | 2; expressFailMsg: string; expressOrder: { mainTrackingNumber: string; labelPath: string[]; totalAmount: number; currency: string; balance: number; }; partnerOrderNumber: string; shipOrderId: string; } // 修改订单响应数据接口 interface ModifyOrderResponseData extends CreateOrderResponseData {} // 订单退款响应数据接口 interface RefundOrderResponseData {} @Provide() export class FreightwavesService { @Inject() logger; // 默认配置 private config: FreightwavesConfig = { appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt', apiBaseUrl: 'https://tms.freightwaves.ca', partner: '25072621035200000060', }; // 初始化配置 setConfig(config: Partial): void { this.config = { ...this.config, ...config }; } // 生成签名 private generateSignature(body: any, date: string): string { const bodyString = JSON.stringify(body); const signatureStr = `${bodyString}${this.config.appSecret}${date}`; return crypto.createHash('md5').update(signatureStr).digest('hex'); } // 发送请求 private async sendRequest(url: string, data: any): Promise> { try { // 设置请求头 - 使用太平洋时间 (America/Los_Angeles) const date = dayjs().tz('America/Los_Angeles').format('YYYY-mm-dd HH:mm:ss'); const headers = { 'Content-Type': 'application/json', 'requestDate': date, 'signature': this.generateSignature(data, date), }; // 记录请求前的详细信息 this.log(`Sending request to: ${this.config.apiBaseUrl}${url}`, { headers, data }); // 发送请求 - 临时禁用SSL证书验证以解决UNABLE_TO_VERIFY_LEAF_SIGNATURE错误 const response = await axios.post>( `${this.config.apiBaseUrl}${url}`, data, { headers, httpsAgent: new (require('https').Agent)({ rejectUnauthorized: false }) } ); // 记录响应信息 this.log(`Received response from: ${this.config.apiBaseUrl}${url}`, { status: response.status, statusText: response.statusText, data: response.data }); // 处理响应 if (response.data.code !== '00000200') { this.log(`Freightwaves API error: ${response.data.msg}`, { url, data, response: response.data }); throw new Error(response.data.msg); } return response.data; } catch (error) { // 更详细的错误记录 if (error.response) { // 请求已发送,服务器返回错误状态码 this.log(`Freightwaves API request failed with status: ${error.response.status}`, { url, data, response: error.response.data, status: error.response.status, headers: error.response.headers }); } else if (error.request) { // 请求已发送,但没有收到响应 this.log(`Freightwaves API request no response received`, { url, data, request: error.request }); } else { // 请求配置时发生错误 this.log(`Freightwaves API request configuration error: ${error.message}`, { url, data, error: error.message }); } throw error; } } /** * 费用试算 * @param params 费用试算参数 * @returns 费用试算结果 */ async rateTry(params: Omit): Promise { const requestData: RateTryRequest = { ...params, partner: this.config.partner, }; const response = await this.sendRequest('/shipService/order/rateTry', requestData); return response.data; } /** * 创建订单 * @param params 创建订单参数 * @returns 创建订单结果 */ async createOrder(params: Omit): Promise { const requestData: CreateOrderRequest = { ...params, partner: this.config.partner, }; const response = await this.sendRequest('/shipService/order/createOrder?apipost_id=0422aa', requestData); return response.data; } /** * 查询订单 * @param params 查询订单参数 * @returns 查询订单结果 */ async queryOrder(params: Omit): Promise { const requestData: QueryOrderRequest = { ...params, partner: this.config.partner, }; const response = await this.sendRequest('/shipService/order/queryOrder', requestData); return response.data; } /** * 修改订单 * @param params 修改订单参数 * @returns 修改订单结果 */ async modifyOrder(params: Omit): Promise { const requestData: ModifyOrderRequest = { ...params, partner: this.config.partner, }; const response = await this.sendRequest('/shipService/order/modifyOrder', requestData); return response.data; } /** * 订单退款 * @param params 订单退款参数 * @returns 订单退款结果 */ async refundOrder(params: Omit): Promise { const requestData: RefundOrderRequest = { ...params, partner: this.config.partner, }; const response = await this.sendRequest('/shipService/order/refundOrder', requestData); return response.data; } /** * 测试创建订单方法 * 用于演示如何使用createOrder方法 */ async testCreateOrder() { try { // 设置必要的配置 this.setConfig({ appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt', apiBaseUrl: 'https://tms.freightwaves.ca', partner: '25072621035200000060' }); // 准备测试数据 const testParams: Omit = { shipCompany: 'DHL', partnerOrderNumber: `test-order-${Date.now()}`, warehouseId: '25072621035200000060', 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 testQueryOrder() { try { // 设置必要的配置 this.setConfig({ appSecret: 'gELCHguGmdTLo!zfihfM91hae8G@9Sz23Mh6pHrt', apiBaseUrl: 'https://tms.freightwaves.ca', partner: '25072621035200000060' }); // 准备测试数据 - 可以通过partnerOrderNumber或shipOrderId查询 const testParams: Omit = { // 选择其中一个参数进行测试 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 日志消息 * @param data 附加数据 */ private log(message: string, data?: any) { if (this.logger) { this.logger.info(message, data); } else { // 如果logger未定义,使用console输出 if (data) { console.log(message, data); } else { console.log(message); } } } }