برنامه نویسی

ادغام 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 کد را قابل آزمایش تر و قابل خواندن تر کرد.
  • متمرکز کردن ارکستراسیون در صورت استفاده ، گسترش سایر حامل ها را تسهیل می کند.
  • پوشش آزمون بدون ترس از رگرسیون در نگهداری کمک می کند.

پایان

راه حل ارائه شده نشان می دهد که چگونه می توان با استفاده از شیوه های خوب معماری ، آزمایش و ورود به سیستم ، یک ادغام قوی با اداره پست ایجاد کرد. علاوه بر حل مشکل پیگیری فوری ، این رویکرد به عنوان پایه ای برای ادغام با سایر شرکای لجستیک عمل می کند.

این اجرای آماده برای تولید ، قابل مشاهده و آسان برای تکامل است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا