feat(mini-user): logistics sign photos, dial, ETA and courier callbacks (v3.4.13)
Extend Courier adapter for XFX sign photos and route callbacks; mini-user logistics UI with timeline, phone dial, and estimated arrival. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -9,6 +9,7 @@ const CMD_SCENE: Record<string, string> = {
|
||||
[XIAOFEIXIA_CMD.ESTIMATE_FREIGHT]: 'ESTIMATE_FREIGHT',
|
||||
[XIAOFEIXIA_CMD.BATCH_GET_ORDER]: 'BATCH_GET_SHIPMENT',
|
||||
[XIAOFEIXIA_CMD.DELIVERY_COVERAGE]: 'CHECK_COVERAGE',
|
||||
[XIAOFEIXIA_CMD.SIGN_PHOTOS]: 'GET_SIGN_PHOTOS',
|
||||
};
|
||||
|
||||
export type CourierLogRef = {
|
||||
|
||||
@@ -29,6 +29,6 @@ import type { ICourierProvider } from './courier.types';
|
||||
},
|
||||
CourierService,
|
||||
],
|
||||
exports: [CourierService, CourierConfigService, COURIER_PROVIDER],
|
||||
exports: [CourierService, CourierConfigService, COURIER_PROVIDER, XiaofeixiaProvider],
|
||||
})
|
||||
export class CourierModule {}
|
||||
|
||||
@@ -12,6 +12,8 @@ import type {
|
||||
ShipmentQuery,
|
||||
TrackCallbackResponse,
|
||||
TrackNode,
|
||||
TrackCallbackPayload,
|
||||
CourierMappedOrderStatus,
|
||||
} from './courier.types';
|
||||
|
||||
/**
|
||||
@@ -45,6 +47,10 @@ export class CourierService {
|
||||
return this.provider.getTrack(query, options);
|
||||
}
|
||||
|
||||
getSignPhotos(query: ShipmentQuery, options?: CourierCallOptions): Promise<string[]> {
|
||||
return this.provider.getSignPhotos(query, options);
|
||||
}
|
||||
|
||||
checkDeliveryCoverage(toAddress: string, options?: CourierCallOptions): Promise<DeliveryCoverageResult> {
|
||||
return this.provider.checkDeliveryCoverage(toAddress, options);
|
||||
}
|
||||
@@ -56,4 +62,12 @@ export class CourierService {
|
||||
buildTrackCallbackResponse(success?: boolean): TrackCallbackResponse {
|
||||
return this.provider.buildTrackCallbackResponse(success);
|
||||
}
|
||||
|
||||
parseTrackCallback(body: unknown): TrackCallbackPayload | null {
|
||||
return this.provider.parseTrackCallback(body);
|
||||
}
|
||||
|
||||
mapTrackStatus(status: string): CourierMappedOrderStatus | null {
|
||||
return this.provider.mapTrackStatus(status);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,13 @@ export interface TrackCallbackResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** 承运商路由回调映射后的订单履约状态 */
|
||||
export type CourierMappedOrderStatus =
|
||||
| 'OUT_WAREHOUSE'
|
||||
| 'SHIPPING'
|
||||
| 'PENDING_RECEIVE'
|
||||
| 'COMPLETED';
|
||||
|
||||
export interface ICourierProvider {
|
||||
readonly code: CourierProviderCode;
|
||||
|
||||
@@ -117,7 +124,10 @@ export interface ICourierProvider {
|
||||
getShipment(query: ShipmentQuery, options?: CourierCallOptions): Promise<ShipmentDetail>;
|
||||
batchGetShipments(query: BatchShipmentQuery, options?: CourierCallOptions): Promise<ShipmentDetail[]>;
|
||||
getTrack(query: ShipmentQuery, options?: CourierCallOptions): Promise<TrackNode[]>;
|
||||
getSignPhotos(query: ShipmentQuery, options?: CourierCallOptions): Promise<string[]>;
|
||||
checkDeliveryCoverage(toAddress: string, options?: CourierCallOptions): Promise<DeliveryCoverageResult>;
|
||||
estimateFreight(weight: number, options?: CourierCallOptions): Promise<FreightEstimateResult>;
|
||||
parseTrackCallback(body: unknown): TrackCallbackPayload | null;
|
||||
mapTrackStatus(status: string): CourierMappedOrderStatus | null;
|
||||
buildTrackCallbackResponse(success?: boolean): TrackCallbackResponse;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ export const XIAOFEIXIA_CMD = {
|
||||
GET_ORDER: '100104',
|
||||
ESTIMATE_FREIGHT: '100105',
|
||||
BATCH_GET_ORDER: '100106',
|
||||
SIGN_PHOTOS: '100108',
|
||||
DELIVERY_COVERAGE: '100301',
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -12,7 +12,9 @@ import type {
|
||||
ShipmentDetail,
|
||||
ShipmentQuery,
|
||||
TrackCallbackResponse,
|
||||
TrackCallbackPayload,
|
||||
TrackNode,
|
||||
CourierMappedOrderStatus,
|
||||
} from '../courier.types';
|
||||
import { XiaofeixiaClient } from './xiaofeixia.client';
|
||||
import { XIAOFEIXIA_CMD } from './xiaofeixia.constants';
|
||||
@@ -121,6 +123,57 @@ export class XiaofeixiaProvider implements ICourierProvider {
|
||||
return data ?? [];
|
||||
}
|
||||
|
||||
async getSignPhotos(query: ShipmentQuery, options?: CourierCallOptions): Promise<string[]> {
|
||||
this.assertShipmentQuery(query);
|
||||
const data = await this.client.request<string[]>(
|
||||
XIAOFEIXIA_CMD.SIGN_PHOTOS,
|
||||
{
|
||||
number: query.trackingNumber,
|
||||
outNumber: query.outNumber,
|
||||
},
|
||||
options?.xiaofeixia,
|
||||
);
|
||||
return (data ?? []).filter((item) => typeof item === 'string' && item.trim().length > 0);
|
||||
}
|
||||
|
||||
parseTrackCallback(body: unknown): TrackCallbackPayload | null {
|
||||
if (!body || typeof body !== 'object') return null;
|
||||
const raw = body as Record<string, unknown>;
|
||||
const outNumber = String(raw.outNumber ?? '').trim();
|
||||
const trackingNumber = String(raw.number ?? raw.trackingNumber ?? '').trim();
|
||||
const status = String(raw.status ?? '').trim();
|
||||
const statusName = String(raw.statusName ?? '').trim();
|
||||
const trackInfo = String(raw.trackInfo ?? '').trim();
|
||||
const createTime = String(raw.createTime ?? '').trim();
|
||||
if (!outNumber && !trackingNumber) return null;
|
||||
if (!status && !trackInfo) return null;
|
||||
return {
|
||||
outNumber,
|
||||
trackingNumber,
|
||||
status,
|
||||
statusName,
|
||||
trackInfo,
|
||||
createTime,
|
||||
};
|
||||
}
|
||||
|
||||
mapTrackStatus(status: string): CourierMappedOrderStatus | null {
|
||||
switch (String(status).trim()) {
|
||||
case '1':
|
||||
return 'OUT_WAREHOUSE';
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
return 'SHIPPING';
|
||||
case '5':
|
||||
return 'PENDING_RECEIVE';
|
||||
case '7':
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async checkDeliveryCoverage(toAddress: string, options?: CourierCallOptions): Promise<DeliveryCoverageResult> {
|
||||
const data = await this.client.request<XiaofeixiaDeliveryCoverageData>(
|
||||
XIAOFEIXIA_CMD.DELIVERY_COVERAGE,
|
||||
|
||||
Reference in New Issue
Block a user