ادغام Correios برای ردیابی تحویل: معماری مقیاس پذیر با Nestjs و Kafka

مقدمه
در سیستم های تجارت الکترونیکی و تدارکات ، ردیابی وضعیت تحویل برای اطمینان از شفافیت و چابکی در خدمات به مشتری ضروری است. خودکار سازی این فرآیند با ادغام مستقیم در پست ، چالشی است که شامل برخورد با API های خارجی ، نقشه برداری وضعیت متناقض و به روزرسانی زمان واقعی است.
در این مقاله ، ما یک رویکرد کامل برای سفارش ارکستراسیون سفارش با استفاده از این موارد ، با استفاده از اصول معماری تمیز ، تست های واحد قوی و ادغام پیام رسان (KAFKA) ارائه خواهیم داد.
طرح راه حل
راه حل حول پرونده استفاده می شود TrackingOrderStatusService
، مسئول:
- تنظیمات حساب را با ردیابی فعال مشاهده کنید.
- درخواست هایی را با وضعیت تمام شده جستجو کنید که باید به روز شود.
- با API اداره پست از طریق ادغام شوید
ProviderTrackingApiGateway
؛ - رویدادهای جدید ثبت نشده را فیلتر کنید.
- رویدادهای نقشه به یک مدل وضعیت داخلی ؛
- وقایع جدید در مخزن ادامه دارد.
- پیام های کافکا در مورد تغییرات وضعیت را صادر کنید.
- وضعیت جدید ناشناخته برای نقشه برداری بعدی را ثبت کنید.
نکات پیچیدگی
1. شناسایی وضعیت
API اداره پست از ترکیبی از codigo
اشمیه tipo
برای نشان دادن وقایع این ترکیب با یاور استاندارد شد:
export function buildPartnerStatusId(codigo: string, tipo: string): string {
return `${codigo}${tipo}`;
}
این تضمین می کند که هر نقطه از سیستم که فقط برای شناسایی وضعیت نیاز دارد ، دارای یک فرم یکپارچه ، کاهش خطاها و تسهیل نقشه برداری است.
2. تشخیص وقایع جدید
برای جلوگیری از پردازش مجدد وقایع که قبلاً ادامه داشت ، ما از این روش استفاده می کنیم getNewHistories
بشر او وقایع حاصل از اداره پست را با رویدادهایی که قبلاً در تاریخ ذخیره شده است مقایسه می کند:
private getNewHistories(tracking: TrackingData[], eventos: CorreiosEventoDTO[]): CorreiosEventoDTO[] {
return eventos.filter(evento => !this.historyExists(evento, tracking));
}
و historyExists
هر دو کد هماهنگ و تاریخ را با هم مقایسه کنید:
private historyExists(evento: CorreiosEventoDTO, tracking: TrackingData[]): boolean {
return tracking.some(hist => {
return (
hist.partner.statusCode === buildPartnerStatusId(evento.codigo, evento.tipo) &&
compareDates(hist.eventDate, evento.dtHrCriado)
);
});
}
3.
اگر یک رویداد اداره پست هیچ وضعیت نقشه برداری نداشته باشد ، ما این وضعیت را برای تجزیه و تحلیل بعدی ذخیره کردیم و توسط کافکا مطلع شدیم:
if (!statusMapped) {
const newStatusPartner = new StatusCodePartner({ statusId, status });
await this.statusCodePartnerRepository.save(newStatusPartner);
await this.messageProvider.sendMessage({
topic: KAFKA_TOPIC_FREIGHT_STATUS_CODE_PARTNER_NOT_FOUND,
messages: [{
key: JSON.stringify({ id: newStatusPartner.id }),
value: { data: { ... } },
}],
headers: { 'X-Tenant-Id': accountId, 'X-Correlation-Id': randomUUID() },
});
}
4. مقاومت و ورود به سیستم
تمام مراحل فرآیند ورود به سیستم متنی:
this.logger.log({
id: 'TrackingOrderStatusService.trackingOrders',
message: `[${tracking.accountId}|${tracking.order.internalOrderId}] Evento processado com sucesso.`,
});
علاوه بر این ، استثنائات برای تسهیل عیب یابی ضبط و وارد سیستم می شوند:
catch (error) {
this.logger.error({
id: 'TrackingOrderStatusService.execute',
message: error.message,
stack: error.stack,
});
}
انتزاع از طریق دروازه API
نکته مهم دیگر اجرای استفاده از آن است ProviderTrackingApiGateway
، رابط کاربری که مصرف API اداره پست را انتزاع می کند. به جای اینکه به طور مستقیم به تماس های HTTP همراه در سرویس همراه باشد ، ادغام از طریق قرارداد تعریف شده توسط قرارداد انجام می شود:
export interface ProviderTrackingApiGateway {
getTracking(etiqueta: string, cep: string): Promise;
}
این رویکرد از اصل وارونگی وابستگی (DIP) پیروی می کند ، اجازه می دهد:
- به راحتی در تست واحد ، اجرای مسخره را به راحتی جایگزین کنید:
correiosApiGateway = mock();
correiosApiGateway.getTracking.mockResolvedValue(mockCorreiosTrackingResponse);
-
به راحتی منشأ ردیابی را تغییر دهید (به عنوان مثال حامل های خصوصی) بدون تغییر پرونده استفاده
-
اتصال را کاهش داده و نگهداری را تسهیل کنید، زیرا منطق ارکستراسیون نیازی به دانستن جزئیات اجرای HTTP ، هدرها یا احراز هویت ندارد
این جدایی واضح بین لایه برنامه و زیرساخت باعث تست قابلیت آزمایش می شود و کد را برای سناریوهای پیچیده تر آماده می کند.
شیوه های خوب اتخاذ شده است
- معکوس وابستگی از طریق نشانه های تزریق.
- جدایی مسئولیت (هر ارائه دهنده یک عملکرد واحد دارد).
- ورود به سیستم با شناسه های متنی (
[accountId|orderId]
) ؛ - استفاده
Promise.all
برای عملیات موازی (وضعیت + پیکربندی) ؛ - پیام های کافکا فقط برای رویدادهای جدید صادر شده است.
تست های واحد
🧪 نمونه دروازه مسخره
در زیر یک نمونه واقعی از نحوه ProviderTrackingApiGateway
با استفاده از تست های واحد قابل شبیه سازی است jest-mock-extended
:
import { mock, MockProxy } from 'jest-mock-extended';
import { ProviderTrackingApiGateway } from '../../correios/interfaces/provider-tracking-api-gateway';
import { CorreiosApiResponseTrackingStatus } from '../../correios/interfaces/provider-tracking-api-gateway';
describe('TrackingOrderStatusService – Integração com Correios API', () => {
let correiosApiGateway: MockProxy<ProviderTrackingApiGateway>;
beforeEach(() => {
correiosApiGateway = mock<ProviderTrackingApiGateway>();
});
it('deve retornar eventos simulados dos Correios via gateway mockado', async () => {
const mockCorreiosResponse: CorreiosApiResponseTrackingStatus = {
objetos: [
{
codObjeto: 'XYZ123456BR',
dtPrevista: new Date('2025-04-25T18:00:00Z'),
modalidade: 'F',
peso: 1.2,
formato: 'Pacote',
eventos: [
{
codigo: 'BDE',
tipo: '01',
dtHrCriado: new Date('2025-04-21T14:00:00Z'),
descricao: 'Objeto entregue ao destinatário',
},
],
},
],
};
correiosApiGateway.getTracking.mockResolvedValue(mockCorreiosResponse);
const result = await correiosApiGateway.getTracking('XYZ123456BR', '12345');
expect(result.objetos[0].codObjeto).toBe('XYZ123456BR');
expect(result.objetos[0].eventos[0].codigo).toBe('BDE');
});
});
این مسخره تضمین می کند که ApiGateway
بدون برقراری تماس واقعی ، جدا شده و قابل آزمایش باشید و امکان شبیه سازی هر نوع بازگشت از API بیرونی را فراهم کنید.
برای اطمینان از کیفیت و قابلیت اطمینان منطق ، آزمایشات اضافه شده است jest-mock-extended
به:
-
getAllStatusMapped
: نقشه برداری صحیح و بازگشت به وضعیت ناشناخته ؛ -
getNewHistories
: تشخیص وقایع جدید ؛ -
historyExists
: مقایسه دقیق بر اساس وضعیت و تاریخ ؛ -
trackingOrders
اشمیهexecute
: سناریوهای کامل موفقیت و شکست.
دروس آموخته شده
- یاران کوچک دوست دارند
buildPartnerStatusId
اشمیهcompareDates
کاهش اشکالات و افزایش استفاده مجدد ؛ - پایداری جدا از حمل و نقل به
sendMessageStatusNotFound
کد را قابل آزمایش تر و قابل خواندن تر کرد. - متمرکز کردن ارکستراسیون در صورت استفاده ، گسترش سایر حامل ها را تسهیل می کند.
- پوشش آزمون بدون ترس از رگرسیون در نگهداری کمک می کند.
پایان
راه حل ارائه شده نشان می دهد که چگونه می توان با استفاده از شیوه های خوب معماری ، آزمایش و ورود به سیستم ، یک ادغام قوی با اداره پست ایجاد کرد. علاوه بر حل مشکل پیگیری فوری ، این رویکرد به عنوان پایه ای برای ادغام با سایر شرکای لجستیک عمل می کند.
این اجرای آماده برای تولید ، قابل مشاهده و آسان برای تکامل است.