forked from yoone/API
1
0
Fork 0

Feature update shipment

物流管理增加订单号
取消运单响应
取消物流二次确认
追踪物流状态
取消运单检测物流检测
This commit is contained in:
longbot 2025-08-12 17:24:06 +08:00 committed by 黄珑
parent cc23f1694d
commit 8e256f67a5
7 changed files with 147 additions and 17 deletions

View File

@ -45,5 +45,8 @@
"url": "" "url": ""
}, },
"author": "anonymous", "author": "anonymous",
"license": "MIT" "license": "MIT",
"devDependencies": {
"@midwayjs/mock": "^3.20.11"
}
} }

View File

@ -221,6 +221,19 @@ export class LogisticsController {
} }
} }
@ApiOkResponse()
@Post('/updateState/:id')
async updateShipmentState(
@Param('shipmentId') shipmentId: string
) {
try {
const data = await this.logisticsService.updateShipmentStateById(shipmentId);
return successResponse(data);
} catch (error) {
return errorResponse(error?.message || '更新运单状态失败')
}
}
@ApiOkResponse() @ApiOkResponse()
@Del('/shipment/:id') @Del('/shipment/:id')
async delShipment(@Param('id') id: string, @User() user) { async delShipment(@Param('id') id: string, @User() user) {

View File

@ -62,6 +62,10 @@ export class Shipment {
@Expose() @Expose()
state?: string; state?: string;
@Column({ nullable: false, default: false })
@Expose()
finished: boolean;
@Column({ nullable: true }) @Column({ nullable: true })
@Expose() @Expose()
type?: string; type?: string;

View File

@ -1,6 +1,9 @@
import { FORMAT, ILogger, Inject, Logger } from '@midwayjs/core'; import { FORMAT, ILogger, Inject, Logger } from '@midwayjs/core';
import { IJob, Job } from '@midwayjs/cron'; import { IJob, Job } from '@midwayjs/cron';
import { LogisticsService } from '../service/logistics.service'; import { LogisticsService } from '../service/logistics.service';
import { Repository } from 'typeorm';
import { Shipment } from '../entity/shipment.entity';
import { InjectEntityModel } from '@midwayjs/typeorm';
@Job({ @Job({
cronTime: FORMAT.CRONTAB.EVERY_PER_30_MINUTE, cronTime: FORMAT.CRONTAB.EVERY_PER_30_MINUTE,
@ -20,3 +23,65 @@ export class SyncShipmentJob implements IJob {
} }
onComplete?(result: any) {} onComplete?(result: any) {}
} }
@Job({
cronTime: '0 0 12 * * *', // 每天12点执行
start: true
})
export class SyncUniuniShipmentJob implements IJob{
@Logger()
logger: ILogger;
@Inject()
logisticsService: LogisticsService;
@InjectEntityModel(Shipment)
shipmentModel: Repository<Shipment>
uniuniStateCodes = {
'190': 'ORDER_RECEIVED',
'192': 'CUSTOM_HOLD',
'195': 'GATEWAY_TRANSIT_OUT',
'198': 'CUSTOM_RELEASE_DIRECT',
'199': 'GATEWAY_TRANSIT',
'200': 'PARCEL_SCANNED',
'202': 'IN_TRANSIT',
'203': 'DELIVERED',
'204': 'TRANSSHIPMENT',
'206': 'WRONG_ADDRESS_FROM_TRANSIT',
'207': 'PARCEL_LOST',
'209': 'OTHER_EXCEPTION',
'211': 'RETURN_OFFICE_FROM_TRANSIT',
'212': 'WRONG_ADDRESS_FROM_RECEIVE',
'213': 'STORAGE_30_DAYS_FROM_OFFICE',
'214': 'STORAGE_30_DAYS_AFTER_SCAN',
'215': 'PARCEL_ABANDONED',
'216': 'SELF_PICK_UP',
'217': 'TRANSSHIPMENT_COMPLETE',
'218': 'SCANNED_PARCEL_MISSING',
'219': 'WRONG_ROUTE_PARCEL',
'220': 'SECOND_DELIVERY',
'221': 'RETURNED_PARCEL_SCANNED',
'222': 'REJECTED_PARCEL_FROM_TRANSIT',
'223': 'CHANGED_ORDER_RESENT',
'224': 'RESENT_ORDER_VOIDED',
'225': 'FORWARDED_3RDPARTY',
'226': 'STORAGE_3RDPARTY_SERVICE_POINT',
'228': 'SECOND_DELIVERED',
'229': 'DROP_OFF_SERVICE_POINTS',
'230': 'RETURN TO SENDER WAREHOUSE',
'231': 'FAILED_DELIVERY_RETRY1',
'232': 'FAILED_DELIVERY_RETRY2',
'255': 'Gateway_To_Gateway_Transit'
};
async onTick() {
const shipments:Shipment[] = await this.shipmentModel.findBy({ finished: false });
shipments.forEach(shipment => {
this.logisticsService.updateShipmentState(shipment);
});
}
onComplete(result: any) {
}
}

View File

@ -121,6 +121,26 @@ export class LogisticsService {
return await this.shippingAddressModel.save(shippingAddress); return await this.shippingAddressModel.save(shippingAddress);
} }
async updateShipmentState(shipment: Shipment) {
try {
const data = await this.uniExpressService.getOrderStatus(shipment.return_tracking_number);
// console.log('res', data, data.data[0].state);
shipment.state = data.data[0].state;
if (shipment.state in [203, 215, 216, 230]) { // todo,写常数
shipment.finished = true;
}
this.shipmentModel.save(shipment);
return shipment.state;
} catch (error) {
throw new Error(`更新运单状态失败 ${error.message}`);
}
}
async updateShipmentStateById(id: string) {
const shipment:Shipment = await this.shipmentModel.findOneBy({ id : id });
return this.updateShipmentState(shipment);
}
async updateShippingAddress(id: number, shippingAddress: ShippingAddress) { async updateShippingAddress(id: number, shippingAddress: ShippingAddress) {
const address = await this.shippingAddressModel.findOneBy({ id }); const address = await this.shippingAddressModel.findOneBy({ id });
if (!address) { if (!address) {
@ -207,11 +227,10 @@ export class LogisticsService {
async removeShipment(shipmentId) { async removeShipment(shipmentId) {
try { try {
const shipment:Shipment = await this.shipmentModel.findOneBy({id: shipmentId}); const shipment:Shipment = await this.shipmentModel.findOneBy({id: shipmentId});
if (shipment.state !== '190') { // todo写常数
throw new Error('订单当前状态无法删除');
}
const order:Order = await this.orderModel.findOneBy({id: shipment.order_id}); const order:Order = await this.orderModel.findOneBy({id: shipment.order_id});
// todo 同步到wooccommerce删除运单信息
const site = await this.geSite(order.siteId);
await this.wpService.deleteShipment(site, order.externalOrderId, shipment.tracking_id);
const dataSource = this.dataSourceManager.getDataSource('default'); const dataSource = this.dataSourceManager.getDataSource('default');
let transactionError = undefined; let transactionError = undefined;
await dataSource.transaction(async manager => { await dataSource.transaction(async manager => {
@ -224,16 +243,7 @@ export class LogisticsService {
shipmentRepo.remove(shipment); shipmentRepo.remove(shipment);
const res = await this.uniExpressService.deleteShipment(shipment.return_tracking_number); const res = await this.uniExpressService.deleteShipment(shipment.return_tracking_number);
console.log('res', res.data); console.log('res', res.data); // todo
// 同步订单状态到woocommerce
if (order.status === OrderStatus.COMPLETED) {
await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.PROCESSING,
});
order.status = OrderStatus.PROCESSING;
}
order.orderStatus = ErpOrderStatus.PROCESSING;
await orderRepo.save(order); await orderRepo.save(order);
@ -245,6 +255,25 @@ export class LogisticsService {
throw new Error(`数据库同步错误: ${transactionError.message}`); throw new Error(`数据库同步错误: ${transactionError.message}`);
} }
try {
// 同步订单状态到woocommerce
const site = await this.geSite(order.siteId);
if (order.status === OrderStatus.COMPLETED) {
await this.wpService.updateOrder(site, order.externalOrderId, {
status: OrderStatus.PROCESSING,
});
order.status = OrderStatus.PROCESSING;
}
order.orderStatus = ErpOrderStatus.PROCESSING;
this.orderModel.save(order);
// todo 同步到wooccommerce删除运单信息
await this.wpService.deleteShipment(site, order.externalOrderId, shipment.tracking_id);
} catch (error) {
console.log('同步到woocommerce失败', error);
return true;
}
return true; return true;
} catch { } catch {
throw new Error('删除运单失败'); throw new Error('删除运单失败');

View File

@ -113,9 +113,7 @@ export class OrderService {
const existingOrder = await this.orderModel.findOne({ const existingOrder = await this.orderModel.findOne({
where: { externalOrderId: order.id, siteId: siteId }, where: { externalOrderId: order.id, siteId: siteId },
}); });
console.log('before save', order);
const orderId = (await this.saveOrder(siteId, orderData)).id; const orderId = (await this.saveOrder(siteId, orderData)).id;
console.log('order_id', orderId);
const externalOrderId = order.id; const externalOrderId = order.id;
if ( if (
existingOrder && existingOrder &&

View File

@ -124,6 +124,7 @@ export class UniExpressService {
headers: { headers: {
'Authorization': `Bearer ${token}` 'Authorization': `Bearer ${token}`
}, },
url: `${this.url}/orders`,
params: { params: {
from, to, page, perPage from, to, page, perPage
} }
@ -135,4 +136,21 @@ export class UniExpressService {
} }
} }
async getOrderStatus(tracking_number: string) {
try {
const key = 'SMq45nJhQuNR3WHsJA6N'; // todo写进常数
const config: AxiosRequestConfig= {
method: 'GET',
url: `${this.url}/orders/trackinguniuni`,
params: {
key,
id: tracking_number
}
};
const res = (await axios.request(config)).data;
return res;
} catch (error) {
console.log(error);
}
}
} }