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; } // 费用试算请求接口 export 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 { msg: string; data: any; } // 查询订单响应数据接口 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: 'http://tms.freightwaves.ca:8901', 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), }; // 发送请求 - 临时禁用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', requestData); return response; } /** * 查询订单 * @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); if (response.code !== '00000200') { throw new Error(response.msg); } 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; } /** * 辅助日志方法,处理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); } } } }