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