| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- import { Router, Request, Response, NextFunction } from 'express';
- import { SpApiClient } from './client.ts';
- import { shopAuthMiddleware } from './middleware/auth.ts';
- import { CustomerFeedbackApi } from './api/customerFeedback.ts';
- import { OrdersApi } from './api/orders.ts';
- import { SalesApi } from './api/sales.ts';
- import { ListingsApi } from './api/listings.ts';
- import { ExternalFulfillmentApi } from './api/externalFulfillment.ts';
- import { CatalogItemsApi } from './api/catalogItems.ts';
- // import { InventoryApi } from './api/inventory.ts';
- // import { PricingApi } from './api/pricing.ts';
- // import { ReportsApi } from './api/reports.ts';
- // import { NotificationsApi } from './api/notifications.ts';
- import { SellersApi } from './api/sellers.ts';
- // Unified Response Format
- const sendResponse = (res: Response, data: any) => {
- res.json({
- success: true,
- data,
- timestamp: new Date().toISOString()
- });
- };
- const asyncHandler = (fn: (req: Request, res: Response, next: NextFunction) => Promise<any>) =>
- (req: Request, res: Response, next: NextFunction) => {
- Promise.resolve(fn(req, res, next)).catch(next);
- };
- /**
- * 创建 SP-API 路由 (动态配置版)
- * 不需要传入静态 client,而是使用中间件动态配置
- */
- export const createSpApiRouter = (staticClient?: SpApiClient) => {
- const router = Router();
-
- // Apply Shop Authentication Middleware to all routes
- router.use(shopAuthMiddleware);
- // Helper to inject client with context
- const getClient = (req: Request): SpApiClient => {
- // We reuse a singleton instance or static methods if possible,
- // but since SpApiClient is now stateless-ish (config via method params),
- // we can use a single instance.
- // However, to keep it clean, let's use the passed staticClient or create a new one.
- // The important part is passing the context to the API methods.
- return staticClient || new SpApiClient();
- };
- // Helper to wrap API calls with context injection
- // Since we modified SpApiClient.request to take context,
- // but the individual API classes (e.g. OrdersApi) wraps the client.request call,
- // we need to pass the context down.
- // OPTION: We can modify the individual API classes to accept context in their methods?
- // OR: We can use a Proxy or Factory to inject context into the client before passing to API.
-
- // Better approach for minimal refactor of API classes:
- // Create a "Scoped Client" that pre-fills the context.
-
- const createScopedApi = <T>(ApiClass: new (client: SpApiClient) => T, req: Request): T => {
- const baseClient = getClient(req);
-
- // Create a proxy client that injects context into request()
- const scopedClient = {
- request: async (options: any) => {
- return baseClient.request({
- ...options,
- context: {
- shopId: req.spApiContext?.shopId,
- marketplaceId: req.spApiContext?.marketplaceId,
- config: req.spApiContext?.config
- }
- });
- }
- } as SpApiClient;
- return new ApiClass(scopedClient);
- };
- // --- Customer Feedback ---
- router.get('/customerFeedback/items/:asin/reviews/topics', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getItemReviewTopics({ ...req.query, asin: req.params.asin } as any);
- sendResponse(res, result);
- }));
-
- router.get('/customerFeedback/items/:asin/browseNode', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getItemBrowseNode({ ...req.query, asin: req.params.asin } as any);
- sendResponse(res, result);
- }));
- router.get('/customerFeedback/browseNodes/:browseNodeId/reviews/topics', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getBrowseNodeReviewTopics({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
- sendResponse(res, result);
- }));
- router.get('/customerFeedback/items/:asin/reviews/trends', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getItemReviewTrends({ ...req.query, asin: req.params.asin } as any);
- sendResponse(res, result);
- }));
- router.get('/customerFeedback/browseNodes/:browseNodeId/reviews/trends', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getBrowseNodeReviewTrends({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
- sendResponse(res, result);
- }));
- router.get('/customerFeedback/browseNodes/:browseNodeId/returns/topics', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getBrowseNodeReturnTopics({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
- sendResponse(res, result);
- }));
- router.get('/customerFeedback/browseNodes/:browseNodeId/returns/trends', asyncHandler(async (req, res) => {
- const api = createScopedApi(CustomerFeedbackApi, req);
- const result = await api.getBrowseNodeReturnTrends({ ...req.query, browseNodeId: req.params.browseNodeId } as any);
- sendResponse(res, result);
- }));
-
- // --- Orders ---
- router.get('/orders', asyncHandler(async (req, res) => {
- const api = createScopedApi(OrdersApi, req);
- const result = await api.getOrders(req.query as any);
- console.log('result', result);
- sendResponse(res, result);
- }));
-
- router.get('/orders/:orderId', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(OrdersApi, req);
- const result = await api.getOrder(req.params.orderId);
- sendResponse(res, result);
- }));
-
- router.get('/orders/:orderId/items', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(OrdersApi, req);
- const result = await api.getOrderItems(req.params.orderId, req.query.NextToken as string);
- sendResponse(res, result);
- }));
- // --- Sales ---
- router.get('/sales/orderMetrics', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(SalesApi, req);
- const result = await api.getOrderMetrics(req.query as any);
- sendResponse(res, result);
- }));
- // --- Listings ---
- router.get('/listings/items/:sellerId', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(ListingsApi, req);
- const result = await api.searchListingsItems({ ...req.query, sellerId: req.params.sellerId } as any);
- sendResponse(res, result);
- }));
-
- router.get('/listings/items/:sellerId/:sku', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(ListingsApi, req);
- const result = await api.getListingsItem({ ...req.query, sellerId: req.params.sellerId, sku: req.params.sku } as any);
- sendResponse(res, result);
- }));
-
- router.put('/listings/items/:sellerId/:sku', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(ListingsApi, req);
- const marketplaceIds = (req.query.marketplaceIds as string)?.split(',') || [];
- const result = await api.putListingsItem(req.params.sellerId, req.params.sku, marketplaceIds, req.body);
- sendResponse(res, result);
- }));
-
- router.delete('/listings/items/:sellerId/:sku', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(ListingsApi, req);
- const marketplaceIds = (req.query.marketplaceIds as string)?.split(',') || [];
- const result = await api.deleteListingsItem(req.params.sellerId, req.params.sku, marketplaceIds);
- sendResponse(res, result);
- }));
- // --- External Fulfillment ---
- router.get('/externalFulfillment/returns', asyncHandler(async (req, res) => {
- const api = createScopedApi(ExternalFulfillmentApi, req);
- const result = await api.listReturns(req.query as any);
- sendResponse(res, result);
- }));
-
- router.get('/externalFulfillment/returns/:returnId', asyncHandler(async (req:any, res) => {
- const api = createScopedApi(ExternalFulfillmentApi, req);
- const result = await api.getReturn(req.params.returnId);
- sendResponse(res, result);
- }));
- // --- Catalog Items ---
- router.get('/catalog/categories', asyncHandler(async (req, res) => {
- const api = createScopedApi(CatalogItemsApi, req);
- const result = await api.listCatalogCategories(req.query as any);
- sendResponse(res, result);
- }));
- router.get('/catalog/items', asyncHandler(async (req, res) => {
- const api = createScopedApi(CatalogItemsApi, req);
- const result = await api.searchCatalogItems(req.query as any);
- sendResponse(res, result);
- }));
- router.get('/catalog/items/:asin', asyncHandler(async (req, res) => {
- const api = createScopedApi(CatalogItemsApi, req);
- const result = await api.getCatalogItem({ ...req.query, asin: req.params.asin } as any);
- sendResponse(res, result);
- }));
- // // --- Inventory ---
- // router.get('/inventory/summaries', asyncHandler(async (req, res) => {
- // const api = createScopedApi(InventoryApi, req);
- // const result = await api.getInventorySummaries(req.query as any);
- // sendResponse(res, result);
- // }));
- // // --- Pricing ---
- // router.get('/pricing/price', asyncHandler(async (req, res) => {
- // const api = createScopedApi(PricingApi, req);
- // const result = await api.getPricing(req.query as any);
- // sendResponse(res, result);
- // }));
- // router.get('/pricing/competitivePrice', asyncHandler(async (req, res) => {
- // const api = createScopedApi(PricingApi, req);
- // const result = await api.getCompetitivePricing(req.query as any);
- // sendResponse(res, result);
- // }));
- // // --- Reports ---
- // router.post('/reports', asyncHandler(async (req, res) => {
- // const api = createScopedApi(ReportsApi, req);
- // const result = await api.createReport(req.body);
- // sendResponse(res, result);
- // }));
- // router.get('/reports/:reportId', asyncHandler(async (req, res) => {
- // const api = createScopedApi(ReportsApi, req);
- // const result = await api.getReport(req.params.reportId);
- // sendResponse(res, result);
- // }));
- // router.get('/reports/documents/:reportDocumentId', asyncHandler(async (req, res) => {
- // const api = createScopedApi(ReportsApi, req);
- // const result = await api.getReportDocument(req.params.reportDocumentId);
- // sendResponse(res, result);
- // }));
- // // --- Notifications ---
- // router.post('/notifications/subscriptions/:notificationType', asyncHandler(async (req, res) => {
- // const api = createScopedApi(NotificationsApi, req);
- // const result = await api.createSubscription(req.params.notificationType, req.body);
- // sendResponse(res, result);
- // }));
- // router.get('/notifications/subscriptions/:notificationType/:subscriptionId', asyncHandler(async (req, res) => {
- // const api = createScopedApi(NotificationsApi, req);
- // const result = await api.getSubscription(req.params.notificationType, req.params.subscriptionId);
- // sendResponse(res, result);
- // }));
- // router.delete('/notifications/subscriptions/:notificationType/:subscriptionId', asyncHandler(async (req, res) => {
- // const api = createScopedApi(NotificationsApi, req);
- // const result = await api.deleteSubscription(req.params.notificationType, req.params.subscriptionId);
- // sendResponse(res, result);
- // }));
- // --- Sellers ---
- router.get('/sellers/account', asyncHandler(async (req, res) => {
- const api = createScopedApi(SellersApi, req);
- const result: any = await api.getAccount();
- console.log(result.data.errors);
- sendResponse(res, result);
- }));
- router.get('/sellers/marketplaceParticipations', asyncHandler(async (req, res) => {
- const api = createScopedApi(SellersApi, req);
- const result = await api.getMarketplaceParticipations();
- sendResponse(res, result);
- }));
- return router;
- };
|