8000 fix: 세븐일레븐 중복 상품의 매장 식별자 보존 by hmmhmmhm · Pull Request #229 · hmmhmmhm/daiso-mcp · GitHub
[go: up one dir, main page]

Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/seveneleven-product-pagination.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

부하를 제한하기 위해 요청당 컬렉션별 100행, 검색어당 최대 5페이지와 원본 합계 500행, 보정 검색 전체 최대 10요청을 허용합니다. 범위가 이 제한을 넘거나 페이지가 반복·누락되면 불완전한 목록을 정상 결과로 반환하지 않습니다. REST는 오류 응답, MCP는 명시적인 `degraded` 응답을 반환합니다. 넓은 검색은 상품명을 더 구체적으로 입력해야 합니다.

Mac 중계는 구형 본문과 공식 본문을 함께 허용합니다. 중계를 먼저 반영한 뒤 Worker를 배포합니다. 중계 인증·GS25 세션·호출 원장은 교체하거나 초기화하지 않습니다. REST 검색 캐시 namespace는 `seveneleven-products-v3`로 갱신했습니다.
Mac 중계는 구형 본문과 공식 본문을 함께 허용합니다. 중계를 먼저 반영한 뒤 Worker를 배포합니다. 중계 인증·GS25 세션·호출 원장은 교체하거나 초기화하지 않습니다. REST 검색 캐시 namespace는 `seveneleven-products-v4`로 갱신했습니다.

확인한 공식 소스: [검색 화면 번들](https://static.7-elevenapp.co.kr/k7app/_nuxt/DH1pJlb5.js), [검색 API 번들](https://static.7-elevenapp.co.kr/k7app/_nuxt/BqIULyu_.js).

같은 상품 코드의 매장 상품과 교환권은 서로 다른 상품 번호를 가질 수 있습니다. 중복 제거는 먼저 확인한 매장 상품 정보를 보존하며, 뒤쪽 교환권의 상품 번호로 덮어쓰지 않습니다.
2 changes: 1 addition & 1 deletion src/api/routes/sevenelevenRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export function registerSevenElevenRoutes(app: Hono<{ Bindings: AppBindings }>):
{
ttlSeconds: 60 * 3,
staleWhileRevalidateSeconds: 60,
keyPrefix: 'seveneleven-products-v3',
keyPrefix: 'seveneleven-products-v4',
},
() => handleSevenElevenSearchProducts(c),
),
Expand Down
12 changes: 7 additions & 5 deletions src/services/seveneleven/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,13 @@ export async function fetchSevenElevenProductSearchResults(
const rows = normalizeProducts(
pagination.documents.length ? pagination.documents : Array.isArray(data.content) ? data.content : [],
);
const products = [
...new Map(
rows.map((product) => [product.itemCode || product.productNo || product.itemName, product]),
).values(),
];
const uniqueProducts = new Map<string, SevenElevenProduct>();
for (const product of rows) {
const key = product.itemCode || product.productNo || product.itemName;
// 뒤쪽 교환권 행이 먼저 확인한 매장 상품 정보를 덮어쓰지 않습니다.
if (!uniqueProducts.has(key)) uniqueProducts.set(key, product);
}
const products = [...uniqueProducts.values()];
return {
query: data.SearchQueryResult?.query || query,
totalCount: products.length,
Expand Down
16 changes: 16 additions & 0 deletions tests/services/seveneleven/pagination.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,19 @@ it('초기 레거시 목록도 전체 500개 제한을 적용한다', async () =
);
await expect(searchSevenElevenProducts({ query: '커피' })).rejects.toThrow('최대 500개 제한');
});

it('교환권 컬렉션의 같은 상품 코드가 먼저 확인한 매장 상품 번호를 덮어쓰지 않는다', async () => {
mockFetch.mockResolvedValueOnce(Response.json({
data: { SearchQueryResult: { Collection: [
{ CollectionId: 'pickup', Documentset: { totalCount: 1, Document: [
{ field: { itemCd: '8809415436006', prdNo: '3047966', itemOnm: '커피', itemGbnNm: '당일픽업', onlinePrice: '2000' } },
] } },
{ CollectionId: 'coupon', Documentset: { totalCount: 1, Document: [
{ field: { itemCd: '8809415436006', prdNo: '21441', itemOnm: '커피', itemGbnNm: '교환권', onlinePrice: '2000' } },
] } },
] } },
}));
const result = await searchSevenElevenProducts({ query: '커피' });
expect(result.totalCount).toBe(1);
expect(result.products[0]).toMatchObject({ productNo: '3047966', itemType: '당일픽업', salePrice: 2000 });
});
Loading
0