- delivery-tracker Docker 이미지 빌드 및 컨테이너 추가 - GraphQL 클라이언트 플러그인 (tracker.js) - parcels CRUD API (등록/조회/수정/삭제/새로고침) - carriers 목록 API - 택배사 ID에서 kr. 접두사 제거, logo_url 추가 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
101 lines
2.6 KiB
JavaScript
101 lines
2.6 KiB
JavaScript
import fp from 'fastify-plugin';
|
|
|
|
const TRACKER_URL = process.env.TRACKER_URL || 'http://delivery-tracker:4000/';
|
|
|
|
// delivery-tracker의 carrier ID는 kr. 접두사 필요
|
|
const CARRIER_ID_MAP = {
|
|
cjlogistics: 'kr.cjlogistics',
|
|
hanjin: 'kr.hanjin',
|
|
lotte: 'kr.lotte',
|
|
epost: 'kr.epost',
|
|
logen: 'kr.logen',
|
|
};
|
|
|
|
const TRACK_QUERY = `
|
|
query Track($carrierId: ID!, $trackingNumber: String!) {
|
|
track(carrierId: $carrierId, trackingNumber: $trackingNumber) {
|
|
trackingNumber
|
|
lastEvent {
|
|
status { code name }
|
|
time
|
|
location { name }
|
|
description
|
|
}
|
|
events(last: 100) {
|
|
edges {
|
|
node {
|
|
status { code name }
|
|
time
|
|
location { name }
|
|
description
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
async function trackerPlugin(fastify) {
|
|
const tracker = {
|
|
async track(carrierId, trackingNumber) {
|
|
const graphqlCarrierId = CARRIER_ID_MAP[carrierId] || `kr.${carrierId}`;
|
|
|
|
const res = await fetch(TRACKER_URL, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
query: TRACK_QUERY,
|
|
variables: {
|
|
carrierId: graphqlCarrierId,
|
|
trackingNumber,
|
|
},
|
|
}),
|
|
});
|
|
|
|
const json = await res.json();
|
|
|
|
if (json.errors) {
|
|
const msg = json.errors[0]?.message || 'tracking failed';
|
|
throw new Error(msg);
|
|
}
|
|
|
|
const trackInfo = json.data?.track;
|
|
if (!trackInfo) {
|
|
return null;
|
|
}
|
|
|
|
// 이벤트 정리
|
|
const events = (trackInfo.events?.edges || []).map((edge) => {
|
|
const node = edge.node;
|
|
return {
|
|
status: node.status?.code || 'UNKNOWN',
|
|
statusName: node.status?.name || '',
|
|
description: node.description || '',
|
|
location: node.location?.name || '',
|
|
time: node.time || null,
|
|
};
|
|
});
|
|
|
|
// 마지막 이벤트
|
|
const lastEvent = trackInfo.lastEvent
|
|
? {
|
|
status: trackInfo.lastEvent.status?.code || 'UNKNOWN',
|
|
statusName: trackInfo.lastEvent.status?.name || '',
|
|
description: trackInfo.lastEvent.description || '',
|
|
location: trackInfo.lastEvent.location?.name || '',
|
|
time: trackInfo.lastEvent.time || null,
|
|
}
|
|
: null;
|
|
|
|
return {
|
|
trackingNumber: trackInfo.trackingNumber,
|
|
lastEvent,
|
|
events,
|
|
};
|
|
},
|
|
};
|
|
|
|
fastify.decorate('tracker', tracker);
|
|
}
|
|
|
|
export default fp(trackerPlugin, { name: 'tracker' });
|