logistics-development

Logistics Adapter Development Guide (物流 Adapter 開發指南)

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "logistics-development" with this command: npx skills add rytass/utils/rytass-utils-logistics-development

Logistics Adapter Development Guide (物流 Adapter 開發指南)

Overview

本指南說明如何基於 @rytass/logistics 基礎套件開發新的物流服務適配器。

Base Package Architecture

@rytass/logistics (Base) ├── LogisticsService<T> # 核心服務介面 ├── LogisticsInterface<T> # 配置介面 ├── LogisticsBaseStatus # 基礎狀態類型 ('DELIVERED' | 'DELIVERING' | 'SHELVED') ├── LogisticsStatus<T> # 泛型狀態類型 ├── LogisticsTraceResponse # 追蹤結果 ├── LogisticsStatusHistory # 狀態歷史 ├── LogisticsErrorInterface # 錯誤介面 ├── LogisticsError # 統一錯誤類別 └── ErrorCode # 錯誤代碼列舉

Core Interfaces

LogisticsService

interface LogisticsService<T extends LogisticsInterface<LogisticsStatus<T>>> { trace(request: string): Promise<LogisticsTraceResponse<T>[]>; trace(request: string[]): Promise<LogisticsTraceResponse<T>[]>; }

LogisticsInterface

interface LogisticsInterface<T = LogisticsBaseStatus> { reference?: T; url: string; }

type LogisticsBaseStatus = 'DELIVERED' | 'DELIVERING' | 'SHELVED';

Response Types

interface LogisticsTraceResponse<K extends LogisticsInterface<LogisticsStatus<K>>> { logisticsId: string; statusHistory: LogisticsStatusHistory<K['reference']>[]; }

interface LogisticsStatusHistory<T> { date: string; status: T; }

interface LogisticsErrorInterface { readonly code: string; readonly message?: string; }

Implementing a New Adapter

Step 1: Define Status Types

// my-logistics-adapter/src/typings.ts export type MyLogisticsStatus = | 'DELIVERED' | 'DELIVERING' | 'SHELVED' | 'PENDING' | 'CANCELLED';

export interface MyLogisticsInterface<T> extends LogisticsInterface<T> { apiKey: string; apiSecret: string; ignoreNotFound?: boolean; }

Step 2: Create Status Map

// my-logistics-adapter/src/constants.ts export const MyLogisticsStatusMap: Record<string, MyLogisticsStatus> = { '已送達': 'DELIVERED', '配送中': 'DELIVERING', '待取': 'SHELVED', '待處理': 'PENDING', '已取消': 'CANCELLED', };

Step 3: Implement Service Class

// my-logistics-adapter/src/my-logistics.service.ts import { LogisticsService, LogisticsTraceResponse, LogisticsError, ErrorCode } from '@rytass/logistics'; import axios from 'axios';

export class MyLogisticsService<T extends MyLogisticsInterface<LogisticsStatus<T>>> implements LogisticsService<T> {

constructor(private readonly configuration: T) {}

async trace(logisticsIds: string | string[]): Promise<LogisticsTraceResponse<T>[]> { const ids = Array.isArray(logisticsIds) ? logisticsIds : [logisticsIds];

return Promise.all(ids.map(id => this.getLogisticsStatus(id)));

}

private async getLogisticsStatus(trackingId: string): Promise<LogisticsTraceResponse<T>> { try { const response = await axios.get(${this.configuration.url}/track/${trackingId}, { headers: { 'X-API-Key': this.configuration.apiKey, 'X-API-Secret': this.configuration.apiSecret, }, });

  if (!response.data.success) {
    if (this.configuration.ignoreNotFound) {
      return { logisticsId: trackingId, statusHistory: [] };
    }
    throw new LogisticsError(ErrorCode.NOT_FOUND_ERROR, `Tracking ${trackingId} not found`);
  }

  return {
    logisticsId: trackingId,
    statusHistory: this.mapStatusHistory(response.data.history),
  };
} catch (error) {
  if (error instanceof LogisticsError) throw error;

  if (axios.isAxiosError(error)) {
    if (error.response?.status === 403) {
      throw new LogisticsError(ErrorCode.PERMISSION_DENIED, 'Invalid API credentials');
    }
    if (error.response?.status === 400) {
      throw new LogisticsError(ErrorCode.INVALID_PARAMETER, 'Invalid tracking number');
    }
  }

  throw new LogisticsError(ErrorCode.NOT_IMPLEMENTED, 'Unknown error');
}

}

private mapStatusHistory(history: any[]): LogisticsStatusHistory<T['reference']>[] { return history.map(item => ({ date: item.timestamp, status: MyLogisticsStatusMap[item.status] || 'DELIVERING', })); } }

Step 4: Export Default Configuration

// my-logistics-adapter/src/index.ts export * from './typings'; export * from './constants'; export * from './my-logistics.service';

export const MyLogistics: MyLogisticsInterface<MyLogisticsStatus> = { url: 'https://api.mylogistics.com/v1', apiKey: '', apiSecret: '', ignoreNotFound: false, };

Error Handling

ErrorCode Reference

Code Constant Usage

999 NOT_IMPLEMENTED 未實現的功能

101 NOT_FOUND_ERROR 找不到追蹤號碼

102 PERMISSION_DENIED API 認證失敗

103 INVALID_PARAMETER 無效的參數

Best Practices

// 1. 使用統一的 LogisticsError throw new LogisticsError(ErrorCode.NOT_FOUND_ERROR, 'Custom message');

// 2. 支援 ignoreNotFound 選項 if (this.configuration.ignoreNotFound) { return { logisticsId: id, statusHistory: [] }; }

// 3. 適當的 HTTP 錯誤映射 if (response.status === 403) { throw new LogisticsError(ErrorCode.PERMISSION_DENIED); }

Testing Guidelines

// tests/my-logistics.spec.ts import { MyLogisticsService, MyLogistics } from '../src'; import { LogisticsError, ErrorCode } from '@rytass/logistics';

describe('MyLogisticsService', () => { const service = new MyLogisticsService({ ...MyLogistics, apiKey: 'test-key', apiSecret: 'test-secret', });

it('should trace single package', async () => { const result = await service.trace('TRACK-001'); expect(result).toHaveLength(1); expect(result[0].logisticsId).toBe('TRACK-001'); });

it('should trace multiple packages', async () => { const result = await service.trace(['TRACK-001', 'TRACK-002']); expect(result).toHaveLength(2); });

it('should handle not found with ignoreNotFound', async () => { const serviceWithIgnore = new MyLogisticsService({ ...MyLogistics, ignoreNotFound: true, }); const result = await serviceWithIgnore.trace('INVALID'); expect(result[0].statusHistory).toHaveLength(0); }); });

Package Structure

my-logistics-adapter/ ├── src/ │ ├── index.ts │ ├── typings.ts │ ├── constants.ts │ └── my-logistics.service.ts ├── tests/ │ └── my-logistics.spec.ts ├── package.json ├── tsconfig.build.json └── README.md

Publishing Checklist

  • 實現 LogisticsService 介面

  • 定義完整的狀態類型

  • 實現錯誤處理

  • 支援批量追蹤

  • 撰寫單元測試

  • 更新 README

  • 遵循 @rytass/logistics-adapter-* 命名規範

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

storage-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

sms-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

payment-development

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

secret-development

No summary provided by upstream source.

Repository SourceNeeds Review