merge(dev): fix XFX track callback body parse
This commit is contained in:
@@ -47,12 +47,14 @@ export class DeliveryCallbackService {
|
|||||||
|
|
||||||
const payload = this.xiaofeixiaProvider.parseTrackCallback(body);
|
const payload = this.xiaofeixiaProvider.parseTrackCallback(body);
|
||||||
if (!payload) {
|
if (!payload) {
|
||||||
|
const response = this.courier.buildTrackCallbackResponse(false);
|
||||||
await logCourierCall(this.prisma, {
|
await logCourierCall(this.prisma, {
|
||||||
...baseLog,
|
...baseLog,
|
||||||
|
responseBody: response,
|
||||||
status: 'FAILED',
|
status: 'FAILED',
|
||||||
errorMessage: '回调体解析失败',
|
errorMessage: '回调体解析失败',
|
||||||
});
|
});
|
||||||
return this.courier.buildTrackCallbackResponse(false);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const order = payload.outNumber
|
const order = payload.outNumber
|
||||||
@@ -64,13 +66,15 @@ export class DeliveryCallbackService {
|
|||||||
: null;
|
: null;
|
||||||
|
|
||||||
if (!order) {
|
if (!order) {
|
||||||
|
const response = this.courier.buildTrackCallbackResponse(false);
|
||||||
await logCourierCall(this.prisma, {
|
await logCourierCall(this.prisma, {
|
||||||
...baseLog,
|
...baseLog,
|
||||||
|
responseBody: response,
|
||||||
status: 'FAILED',
|
status: 'FAILED',
|
||||||
errorMessage: '订单不存在',
|
errorMessage: '订单不存在',
|
||||||
externalNo: payload.outNumber || payload.trackingNumber,
|
externalNo: payload.outNumber || payload.trackingNumber,
|
||||||
});
|
});
|
||||||
return this.courier.buildTrackCallbackResponse(false);
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targetStatus = this.xiaofeixiaProvider.mapTrackStatus(
|
const targetStatus = this.xiaofeixiaProvider.mapTrackStatus(
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Body, Controller, Param, Post } from '@nestjs/common';
|
import { Body, Controller, Param, Post, Res } from '@nestjs/common';
|
||||||
|
import type { Response } from 'express';
|
||||||
import { DeliveryCallbackService } from './delivery-callback.service';
|
import { DeliveryCallbackService } from './delivery-callback.service';
|
||||||
|
|
||||||
@Controller('callbacks')
|
@Controller('callbacks')
|
||||||
@@ -7,21 +8,28 @@ export class DeliveryCallbackController {
|
|||||||
|
|
||||||
/** 小飞侠/物流路由变化回调(适配器入口) */
|
/** 小飞侠/物流路由变化回调(适配器入口) */
|
||||||
@Post('courier/:provider/track')
|
@Post('courier/:provider/track')
|
||||||
trackByProvider(@Param('provider') provider: string, @Body() body: unknown) {
|
async trackByProvider(
|
||||||
return this.deliveryCallbackService.handleTrackCallback(
|
@Param('provider') provider: string,
|
||||||
|
@Body() body: unknown,
|
||||||
|
@Res() res: Response,
|
||||||
|
) {
|
||||||
|
const result = await this.deliveryCallbackService.handleTrackCallback(
|
||||||
provider,
|
provider,
|
||||||
body,
|
body,
|
||||||
`/api/v1/callbacks/courier/${provider}/track`,
|
`/api/v1/callbacks/courier/${provider}/track`,
|
||||||
);
|
);
|
||||||
|
// 直出承运商约定结构,避免被全局 { code:0, data } 包装
|
||||||
|
return res.status(200).json(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 兼容旧路径,默认按小飞侠解析 */
|
/** 兼容旧路径,默认按小飞侠解析 */
|
||||||
@Post('delivery/track')
|
@Post('delivery/track')
|
||||||
trackLegacy(@Body() body: unknown) {
|
async trackLegacy(@Body() body: unknown, @Res() res: Response) {
|
||||||
return this.deliveryCallbackService.handleTrackCallback(
|
const result = await this.deliveryCallbackService.handleTrackCallback(
|
||||||
'xfx',
|
'xfx',
|
||||||
body,
|
body,
|
||||||
'/api/v1/callbacks/delivery/track',
|
'/api/v1/callbacks/delivery/track',
|
||||||
);
|
);
|
||||||
|
return res.status(200).json(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,15 @@ export class XiaofeixiaProvider implements ICourierProvider {
|
|||||||
|
|
||||||
parseTrackCallback(body: unknown): TrackCallbackPayload | null {
|
parseTrackCallback(body: unknown): TrackCallbackPayload | null {
|
||||||
if (!body || typeof body !== 'object') return null;
|
if (!body || typeof body !== 'object') return null;
|
||||||
const raw = body as Record<string, unknown>;
|
const root = body as Record<string, unknown>;
|
||||||
|
// 兼容顶层字段或 data/payload 包裹
|
||||||
|
const nested =
|
||||||
|
root.data && typeof root.data === 'object'
|
||||||
|
? (root.data as Record<string, unknown>)
|
||||||
|
: root.payload && typeof root.payload === 'object'
|
||||||
|
? (root.payload as Record<string, unknown>)
|
||||||
|
: null;
|
||||||
|
const raw = nested ? { ...nested, ...root } : root;
|
||||||
const outNumber = String(raw.outNumber ?? '').trim();
|
const outNumber = String(raw.outNumber ?? '').trim();
|
||||||
const trackingNumber = String(raw.number ?? raw.trackingNumber ?? '').trim();
|
const trackingNumber = String(raw.number ?? raw.trackingNumber ?? '').trim();
|
||||||
const status = String(raw.status ?? '').trim();
|
const status = String(raw.status ?? '').trim();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { NestFactory } from '@nestjs/core';
|
|||||||
import { loadAppConfig } from '@dukang/shared-types';
|
import { loadAppConfig } from '@dukang/shared-types';
|
||||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
import { ValidationPipe } from '@nestjs/common';
|
import { ValidationPipe } from '@nestjs/common';
|
||||||
import { json } from 'express';
|
import { json, urlencoded } from 'express';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
||||||
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
||||||
@@ -26,6 +26,7 @@ async function bootstrap() {
|
|||||||
app.setGlobalPrefix('api/v1');
|
app.setGlobalPrefix('api/v1');
|
||||||
app.set('trust proxy', true);
|
app.set('trust proxy', true);
|
||||||
app.enableCors({ origin: true, credentials: true });
|
app.enableCors({ origin: true, credentials: true });
|
||||||
|
// 微信支付等需 rawBody;小飞侠回调多为 x-www-form-urlencoded
|
||||||
app.use(
|
app.use(
|
||||||
json({
|
json({
|
||||||
verify: (req, _res, buf) => {
|
verify: (req, _res, buf) => {
|
||||||
@@ -39,6 +40,7 @@ async function bootstrap() {
|
|||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
app.use(urlencoded({ extended: true }));
|
||||||
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true }));
|
||||||
app.useGlobalFilters(new HttpExceptionFilter(app.get(AlertService)));
|
app.useGlobalFilters(new HttpExceptionFilter(app.get(AlertService)));
|
||||||
app.useGlobalInterceptors(new ResponseInterceptor(), app.get(LoggingInterceptor));
|
app.useGlobalInterceptors(new ResponseInterceptor(), app.get(LoggingInterceptor));
|
||||||
|
|||||||
Reference in New Issue
Block a user