forked from yoone/API
Compare commits
6 Commits
c68b7583af
...
ac17f9f224
| Author | SHA1 | Date |
|---|---|---|
|
|
ac17f9f224 | |
|
|
8c617d14e0 | |
|
|
72b28e7c2b | |
|
|
d884369915 | |
|
|
0057585da3 | |
|
|
5ca3c94afc |
|
|
@ -21,6 +21,7 @@
|
|||
"@midwayjs/swagger": "^3.20.2",
|
||||
"@midwayjs/typeorm": "^3.20.0",
|
||||
"@midwayjs/validate": "^3.20.2",
|
||||
"@woocommerce/woocommerce-rest-api": "^1.0.2",
|
||||
"axios": "^1.7.9",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
|
|
@ -675,6 +676,21 @@
|
|||
"@types/superagent": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@woocommerce/woocommerce-rest-api": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@woocommerce/woocommerce-rest-api/-/woocommerce-rest-api-1.0.2.tgz",
|
||||
"integrity": "sha512-G+0VwM0MINF83KnT7Rg/htm9EEYADWvDPT/UWEJdZ0de1vXvsPrr4M1ksKaxgKHO8qIJViRrIHCtrui2JoVA+Q==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.8",
|
||||
"create-hmac": "^1.1.7",
|
||||
"oauth-1.0a": "^2.2.6",
|
||||
"url-parse": "^1.4.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmmirror.com/accepts/-/accepts-1.3.8.tgz",
|
||||
|
|
@ -1017,6 +1033,20 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/cipher-base": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.7.tgz",
|
||||
"integrity": "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"to-buffer": "^1.2.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/class-transformer": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmmirror.com/class-transformer/-/class-transformer-0.5.1.tgz",
|
||||
|
|
@ -1301,6 +1331,39 @@
|
|||
"integrity": "sha512-3DdaFaU/Zf1AnpLiFDeNCD4TOWe3Zl2RZaTzUvWiIk5ERzcCodOE20Vqq4fzCbNoHURFHT4/us/Lfq+S2zyY4w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/create-hash": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz",
|
||||
"integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.1",
|
||||
"inherits": "^2.0.1",
|
||||
"md5.js": "^1.3.4",
|
||||
"ripemd160": "^2.0.1",
|
||||
"sha.js": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-hmac": {
|
||||
"version": "1.1.7",
|
||||
"resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz",
|
||||
"integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cipher-base": "^1.0.3",
|
||||
"create-hash": "^1.1.0",
|
||||
"inherits": "^2.0.1",
|
||||
"ripemd160": "^2.0.0",
|
||||
"safe-buffer": "^5.0.1",
|
||||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"node_modules/cron": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmmirror.com/cron/-/cron-3.5.0.tgz",
|
||||
|
|
@ -1928,6 +1991,21 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hash-base": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.2.tgz",
|
||||
"integrity": "sha512-Bb33KbowVTIj5s7Ked1OsqHUeCpz//tPwR+E2zJgJKo9Z5XolZ9b6bdUgjmYlwnWhoOQKoTd1TYToZGn5mAYOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.4",
|
||||
"readable-stream": "^2.3.8",
|
||||
"safe-buffer": "^5.2.1",
|
||||
"to-buffer": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz",
|
||||
|
|
@ -2487,6 +2565,17 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/md5.js": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz",
|
||||
"integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hash-base": "^3.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"safe-buffer": "^5.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/media-typer/-/media-typer-0.3.0.tgz",
|
||||
|
|
@ -2718,6 +2807,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/oauth-1.0a": {
|
||||
"version": "2.2.6",
|
||||
"resolved": "https://registry.npmjs.org/oauth-1.0a/-/oauth-1.0a-2.2.6.tgz",
|
||||
"integrity": "sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
|
|
@ -2853,6 +2948,12 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/proxy-from-env": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
|
||||
|
|
@ -2874,6 +2975,12 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/querystringify": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
||||
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/queue-lit": {
|
||||
"version": "1.5.2",
|
||||
"resolved": "https://registry.npmmirror.com/queue-lit/-/queue-lit-1.5.2.tgz",
|
||||
|
|
@ -2932,6 +3039,33 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readdirp": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
|
||||
|
|
@ -2960,6 +3094,12 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/resolve-pkg-maps": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmmirror.com/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||
|
|
@ -2981,6 +3121,19 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/ripemd160": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.3.tgz",
|
||||
"integrity": "sha512-5Di9UC0+8h1L6ZD2d7awM7E/T4uA1fJRlx6zk/NvdCCVEoAnFqvHmCuNeIKoCeIixBX/q8uM+6ycDvF8woqosA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"hash-base": "^3.1.2",
|
||||
"inherits": "^2.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/run-parallel": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz",
|
||||
|
|
@ -3305,6 +3458,21 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/string_decoder/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string-width": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz",
|
||||
|
|
@ -3467,9 +3635,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/to-buffer": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmmirror.com/to-buffer/-/to-buffer-1.2.1.tgz",
|
||||
"integrity": "sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==",
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz",
|
||||
"integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"isarray": "^2.0.5",
|
||||
|
|
@ -3697,6 +3865,22 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/url-parse": {
|
||||
"version": "1.5.10",
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
||||
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"querystringify": "^2.1.1",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "11.1.0",
|
||||
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-11.1.0.tgz",
|
||||
|
|
|
|||
|
|
@ -78,8 +78,5 @@ export class MainConfiguration {
|
|||
}
|
||||
}
|
||||
);
|
||||
|
||||
const sites = this.app.getConfig('wpSite') || [];
|
||||
await this.siteService.syncFromConfig(sites);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,9 +19,15 @@ export class ShipmentBookDTO {
|
|||
@ApiProperty({ type: 'number', isArray: true })
|
||||
@Rule(RuleType.array<number>().default([]))
|
||||
orderIds?: number[];
|
||||
|
||||
@ApiProperty()
|
||||
shipmentPlatform: string;
|
||||
}
|
||||
|
||||
export class ShipmentFeeBookDTO {
|
||||
|
||||
@ApiProperty()
|
||||
shipmentPlatform: string;
|
||||
@ApiProperty()
|
||||
stockPointId: number;
|
||||
@ApiProperty()
|
||||
|
|
@ -63,6 +69,8 @@ export class ShipmentFeeBookDTO {
|
|||
weight: number;
|
||||
@ApiProperty()
|
||||
weightUom: string;
|
||||
@ApiProperty()
|
||||
address_id: number;
|
||||
}
|
||||
|
||||
export class PaymentMethodDTO {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ export enum OrderStatus {
|
|||
REFUNDED = 'refunded', // 已退款
|
||||
FAILED = 'failed', // 失败订单
|
||||
DRAFT = 'draft', // 草稿
|
||||
AUTO_DRAFT = 'auto-draft', // 自动草稿
|
||||
// TRASH = 'trash',
|
||||
// refund 也就是退款相关的状态
|
||||
RETURN_REQUESTED = 'return-requested', // 已申请退款
|
||||
|
|
|
|||
|
|
@ -0,0 +1,40 @@
|
|||
import { ILogger, Inject, Logger } from '@midwayjs/core';
|
||||
import { IJob, Job } from '@midwayjs/cron';
|
||||
import { LogisticsService } from '../service/logistics.service';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Shipment } from '../entity/shipment.entity';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
|
||||
|
||||
@Job({
|
||||
cronTime: '0 0 12 * * *', // 每天12点执行
|
||||
start: true
|
||||
})
|
||||
export class SyncTmsJob implements IJob {
|
||||
@Logger()
|
||||
logger: ILogger;
|
||||
|
||||
@Inject()
|
||||
logisticsService: LogisticsService;
|
||||
|
||||
@InjectEntityModel(Shipment)
|
||||
shipmentModel: Repository<Shipment>
|
||||
|
||||
async onTick() {
|
||||
const shipments:Shipment[] = await this.shipmentModel.findBy({ tracking_provider: 'freightwaves',finished: false });
|
||||
const results = await Promise.all(
|
||||
shipments.map(async shipment => {
|
||||
return await this.logisticsService.updateFreightwavesShipmentState(shipment);
|
||||
})
|
||||
)
|
||||
this.logger.info(`更新运单状态完毕 ${JSON.stringify(results)}`);
|
||||
return results
|
||||
}
|
||||
|
||||
onComplete(result: any) {
|
||||
this.logger.info(`更新运单状态完成 ${result}`);
|
||||
}
|
||||
onError(error: any) {
|
||||
this.logger.error(`更新运单状态失败 ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,578 @@
|
|||
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<T> {
|
||||
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<FreightwavesConfig>): 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<T>(url: string, data: any): Promise<FreightwavesResponse<T>> {
|
||||
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),
|
||||
};
|
||||
|
||||
// 记录请求前的详细信息
|
||||
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}`,
|
||||
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<RateTryRequest, 'partner'>): Promise<RateTryResponseData> {
|
||||
const requestData: RateTryRequest = {
|
||||
...params,
|
||||
partner: this.config.partner,
|
||||
};
|
||||
|
||||
const response = await this.sendRequest<RateTryResponseData>('/shipService/order/rateTry', requestData);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建订单
|
||||
* @param params 创建订单参数
|
||||
* @returns 创建订单结果
|
||||
*/
|
||||
async createOrder(params: Omit<CreateOrderRequest, 'partner'>): Promise<CreateOrderResponseData> {
|
||||
const requestData: CreateOrderRequest = {
|
||||
...params,
|
||||
partner: this.config.partner,
|
||||
};
|
||||
|
||||
const response = await this.sendRequest<CreateOrderResponseData>('shipService/order/rateTry', requestData);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询订单
|
||||
* @param params 查询订单参数
|
||||
* @returns 查询订单结果
|
||||
*/
|
||||
async queryOrder(params: Omit<QueryOrderRequest, 'partner'>): Promise<QueryOrderResponseData> {
|
||||
const requestData: QueryOrderRequest = {
|
||||
...params,
|
||||
partner: this.config.partner,
|
||||
};
|
||||
|
||||
const response = await this.sendRequest<QueryOrderResponseData>('/shipService/order/queryOrder', requestData);
|
||||
if (response.code !== '00000200') {
|
||||
throw new Error(response.msg);
|
||||
}
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改订单
|
||||
* @param params 修改订单参数
|
||||
* @returns 修改订单结果
|
||||
*/
|
||||
async modifyOrder(params: Omit<ModifyOrderRequest, 'partner'>): Promise<ModifyOrderResponseData> {
|
||||
const requestData: ModifyOrderRequest = {
|
||||
...params,
|
||||
partner: this.config.partner,
|
||||
};
|
||||
|
||||
const response = await this.sendRequest<ModifyOrderResponseData>('/shipService/order/modifyOrder', requestData);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单退款
|
||||
* @param params 订单退款参数
|
||||
* @returns 订单退款结果
|
||||
*/
|
||||
async refundOrder(params: Omit<RefundOrderRequest, 'partner'>): Promise<RefundOrderResponseData> {
|
||||
const requestData: RefundOrderRequest = {
|
||||
...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 日志消息
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,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 { FreightwavesService, RateTryRequest } from './freightwaves.service';
|
||||
@Provide()
|
||||
export class LogisticsService {
|
||||
@InjectEntityModel(Service)
|
||||
|
|
@ -73,6 +73,9 @@ export class LogisticsService {
|
|||
@Inject()
|
||||
uniExpressService: UniExpressService;
|
||||
|
||||
@Inject()
|
||||
freightwavesService: FreightwavesService;
|
||||
|
||||
@Inject()
|
||||
wpService: WPService;
|
||||
|
||||
|
|
@ -137,6 +140,31 @@ export class LogisticsService {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
//"expressFinish": 0, //是否快递创建完成(1:完成 0:未完成,需要轮询 2:失败)
|
||||
async updateFreightwavesShipmentState(shipment: Shipment) {
|
||||
try {
|
||||
const data = await this.freightwavesService.queryOrder({ shipOrderId: shipment.order_id.toString() });
|
||||
console.log('updateFreightwavesShipmentState data:', data);
|
||||
// huo
|
||||
if (data.expressFinish === 2) {
|
||||
throw new Error('获取运单状态失败,原因为' + data.expressFailMsg)
|
||||
}
|
||||
|
||||
if (data.expressFinish === 0) {
|
||||
shipment.state = '203';
|
||||
shipment.finished = true;
|
||||
}
|
||||
|
||||
this.shipmentModel.save(shipment);
|
||||
return shipment.state;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
// throw new Error(`更新运单状态失败 ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async updateShipmentStateById(id: number) {
|
||||
const shipment: Shipment = await this.shipmentModel.findOneBy({ id: id });
|
||||
return this.updateShipmentState(shipment);
|
||||
|
|
@ -315,39 +343,40 @@ 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 = this.mepShipment(data);
|
||||
// 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.uniExpressService.createShipment(reqBody);
|
||||
// // 添加运单
|
||||
// resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
|
||||
|
||||
// 记录物流信息,并将订单状态转到完成
|
||||
// // 记录物流信息,并将订单状态转到完成
|
||||
if (resShipmentOrder.status === 'SUCCESS') {
|
||||
order.orderStatus = ErpOrderStatus.COMPLETED;
|
||||
} else {
|
||||
|
|
@ -638,4 +667,209 @@ export class LogisticsService {
|
|||
|
||||
return { items, total, current, pageSize };
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async mepShipment(data: ShipmentBookDTO) {
|
||||
try {
|
||||
const stock_point = await this.stockPointModel.findOneBy({ id: data.stockPointId });
|
||||
let resShipmentOrder;
|
||||
|
||||
if (data.shipmentPlatform === 'uniuni') {
|
||||
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': '' // todo: 需要获取订单的externalOrderId
|
||||
}
|
||||
};
|
||||
|
||||
// 添加运单
|
||||
resShipmentOrder = await this.uniExpressService.createShipment(reqBody);
|
||||
}
|
||||
|
||||
if (data.shipmentPlatform === 'freightwaves') {
|
||||
// 根据TMS系统对接说明文档格式化参数
|
||||
const reqBody = {
|
||||
shipCompany: '', // 渠道(可以不指定)
|
||||
partnerOrderNumber: `order-${Date.now()}`, // 第三方客户订单编号(唯一)
|
||||
warehouseId: String(stock_point.upStreamStockPointId), // 发货仓库编号(转换为字符串类型)
|
||||
shipper: {
|
||||
name: data.details.origin.contact_name, // 姓名
|
||||
phone: data.details.origin.phone_number.number, // 电话(提取number属性转换为字符串)
|
||||
company: '', // 公司
|
||||
countryCode: data.details.origin.address.country, // 国家Code
|
||||
city: data.details.origin.address.city, // 城市
|
||||
state: data.details.origin.address.region, // 州/省Code,两个字母缩写
|
||||
address1: data.details.origin.address.address_line_1, // 详细地址
|
||||
address2: '', // 详细地址2(Address类型中没有address_line_2属性)
|
||||
postCode: data.details.origin.address.postal_code.replace(/\s/g, ''), // 邮编
|
||||
countryName: data.details.origin.address.country, // 国家名称(Address类型中没有country_name属性,使用country代替)
|
||||
cityName: data.details.origin.address.city, // 城市名称
|
||||
stateName: data.details.origin.address.region, // 州/省名称
|
||||
companyName: '' // 公司名称
|
||||
},
|
||||
reciver: {
|
||||
name: data.details.destination.contact_name, // 姓名
|
||||
phone: data.details.destination.phone_number.number, // 电话
|
||||
company: '', // 公司
|
||||
countryCode: data.details.destination.address.country, // 国家Code
|
||||
city: data.details.destination.address.city, // 城市
|
||||
state: data.details.destination.address.region, // 州/省Code,两个字母的缩写
|
||||
address1: data.details.destination.address.address_line_1, // 详细地址
|
||||
address2: '', // 详细地址2(Address类型中没有address_line_2属性)
|
||||
postCode: data.details.destination.address.postal_code.replace(/\s/g, ''), // 邮编
|
||||
countryName: data.details.destination.address.country, // 国家名称(Address类型中没有country_name属性,使用country代替)
|
||||
cityName: data.details.destination.address.city, // 城市名称
|
||||
stateName: data.details.destination.address.region, // 州/省名称
|
||||
companyName: '' // 公司名称
|
||||
},
|
||||
packages: [
|
||||
{
|
||||
dimensions: {
|
||||
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, // 高
|
||||
lengthUnit: (data.details.packaging_properties.packages[0].measurements.cuboid.unit === 'cm' ? 'CM' : 'IN') as 'CM' | 'IN', // 长度单位(IN,CM)
|
||||
weight: data.details.packaging_properties.packages[0].measurements.weight.value, // 重量
|
||||
weightUnit: (data.details.packaging_properties.packages[0].measurements.weight.unit === 'kg' ? 'KG' : 'LB') as 'KG' | 'LB' // 重量单位(LB,KG)
|
||||
},
|
||||
currency: 'CAD', // 币种(默认CAD)
|
||||
description: 'Package' // 包裹描述(确保是字符串类型)
|
||||
}
|
||||
],
|
||||
signService: 0 as 0 | 1, // 签名服务 0不使用, 1使用
|
||||
declaration: {
|
||||
boxNo: 'BOX-001', // 箱号
|
||||
sku: 'DEFAULT-SKU', // SKU
|
||||
cnname: '默认商品', // 中文名称
|
||||
enname: 'Default Product', // 英文名称
|
||||
declaredPrice: 0, // 申报价格
|
||||
declaredQty: 1, // 申报数量
|
||||
material: 'General', // 材质
|
||||
intendedUse: 'General use', // 用途
|
||||
cweight: data.details.packaging_properties.packages[0].measurements.weight.value, // 重量
|
||||
hsCode: '39269090', // 海关编码
|
||||
battery: 'No' // 是否含电池
|
||||
}
|
||||
};
|
||||
|
||||
// 调用freightwaves费用试算或创建订单API
|
||||
// 注意:根据实际需要调用对应的方法
|
||||
// resShipmentOrder = await this.freightwavesService.rateTry(reqBody); // 费用试算
|
||||
resShipmentOrder = await this.freightwavesService.createOrder(reqBody); // 创建订单
|
||||
}
|
||||
|
||||
return resShipmentOrder;
|
||||
} catch (error) {
|
||||
console.log('物流订单处理失败:', error); // 使用console.log代替this.log
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将ShipmentFeeBookDTO转换为freightwaves的RateTryRequest格式
|
||||
* @param data ShipmentFeeBookDTO数据
|
||||
* @returns RateTryRequest格式的数据
|
||||
*/
|
||||
convertToFreightwavesRateTry(data: ShipmentFeeBookDTO): Omit<RateTryRequest, 'partner'> {
|
||||
// 转换为RateTryRequest格式
|
||||
return {
|
||||
shipCompany: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
partnerOrderNumber: `order-${Date.now()}`, // 必填,使用时间戳生成
|
||||
warehouseId: String(data.stockPointId), // 可选,使用stockPointId转换
|
||||
shipper: {
|
||||
name: data.sender, // 必填
|
||||
phone: data.startPhone, // 必填
|
||||
company: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
countryCode: data.shipperCountryCode, // 必填
|
||||
city: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
state: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
address1: data.pickupAddress, // 必填
|
||||
address2: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
postCode: data.startPostalCode, // 必填
|
||||
countryName: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
cityName: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
stateName: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
companyName: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
},
|
||||
reciver: {
|
||||
name: data.receiver, // 必填
|
||||
phone: data.receiverPhone, // 必填
|
||||
company: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
countryCode: data.country, // 必填,使用country代替countryCode
|
||||
city: data.city, // 必填
|
||||
state: data.province, // 必填,使用province代替state
|
||||
address1: data.deliveryAddress, // 必填
|
||||
address2: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
postCode: data.postalCode, // 必填
|
||||
countryName: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
cityName: data.city, // 必填,使用city代替cityName
|
||||
stateName: data.province, // 必填,使用province代替stateName
|
||||
companyName: '', // 必填,但ShipmentFeeBookDTO中缺少
|
||||
},
|
||||
packages: [
|
||||
{
|
||||
dimensions: {
|
||||
length: data.length, // 必填
|
||||
width: data.width, // 必填
|
||||
height: data.height, // 必填
|
||||
lengthUnit: (data.dimensionUom.toUpperCase() === 'CM' ? 'CM' : 'IN') as 'CM' | 'IN', // 必填,转换为有效的单位
|
||||
weight: data.weight, // 必填
|
||||
weightUnit: (data.weightUom.toUpperCase() === 'KG' ? 'KG' : 'LB') as 'KG' | 'LB', // 必填,转换为有效的单位
|
||||
},
|
||||
currency: 'CAD', // 必填,但ShipmentFeeBookDTO中缺少,使用默认值
|
||||
description: 'Package', // 必填,但ShipmentFeeBookDTO中缺少,使用默认值
|
||||
},
|
||||
],
|
||||
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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,13 @@ export class OrderService {
|
|||
});
|
||||
// 更新状态
|
||||
await this.autoUpdateOrderStatus(siteId, order);
|
||||
if (order.status === OrderStatus.AUTO_DRAFT) {
|
||||
return;
|
||||
}
|
||||
// 更新订单
|
||||
if (existingOrder) {
|
||||
await this.orderModel.update({ id: existingOrder.id }, { orderStatus: this.mapOrderStatus(order.status) });
|
||||
}
|
||||
const externalOrderId = order.id;
|
||||
if (
|
||||
existingOrder &&
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { Provide, Scope, ScopeEnum } from '@midwayjs/core';
|
|||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository, Like, In } from 'typeorm';
|
||||
import { Site } from '../entity/site.entity';
|
||||
import { WpSite } from '../interface';
|
||||
import { UpdateSiteDTO } from '../dto/site.dto';
|
||||
|
||||
@Provide()
|
||||
|
|
@ -11,25 +10,6 @@ export class SiteService {
|
|||
@InjectEntityModel(Site)
|
||||
siteModel: Repository<Site>;
|
||||
|
||||
async syncFromConfig(sites: WpSite[] = []) {
|
||||
// 将配置中的 WpSite 同步到数据库 Site 表(用于一次性导入或初始化)
|
||||
for (const siteConfig of sites) {
|
||||
// 按站点名称查询是否已存在记录
|
||||
const exist = await this.siteModel.findOne({ where: { siteName: siteConfig.siteName } });
|
||||
// 将 WpSite 字段映射为 Site 实体字段
|
||||
const payload: Partial<Site> = {
|
||||
siteName: siteConfig.siteName,
|
||||
apiUrl: (siteConfig as any).wpApiUrl,
|
||||
consumerKey: (siteConfig as any).consumerKey,
|
||||
consumerSecret: (siteConfig as any).consumerSecret,
|
||||
type: 'woocommerce',
|
||||
};
|
||||
// 存在则更新,不存在则插入新记录
|
||||
if (exist) await this.siteModel.update({ id: exist.id }, payload);
|
||||
else await this.siteModel.insert(payload as Site);
|
||||
}
|
||||
}
|
||||
|
||||
async create(data: Partial<Site>) {
|
||||
// 创建新的站点记录
|
||||
await this.siteModel.insert(data as Site);
|
||||
|
|
|
|||
Loading…
Reference in New Issue