import { getPayload } from 'payload' import config from '@payload-config' import { NextResponse } from 'next/server' import { getAllMedusaProducts, transformMedusaProductToPayload, getMedusaProductsPaginated, getProductCollection, } from '@/lib/medusa' /** * 同步 Medusa 商品到 Payload CMS * GET /api/sync-medusa * GET /api/sync-medusa?medusaId=prod_xxx (通过 Medusa ID 同步单个商品) * GET /api/sync-medusa?payloadId=123 (通过 Payload ID 同步单个商品) */ export async function GET(request: Request) { try { const { searchParams } = new URL(request.url) const medusaId = searchParams.get('medusaId') const payloadId = searchParams.get('payloadId') const collection = searchParams.get('collection') const forceUpdate = searchParams.get('forceUpdate') === 'true' const payload = await getPayload({ config }) // 同步单个商品(通过 Medusa ID) if (medusaId) { const result = await syncSingleProductByMedusaId(payload, medusaId, forceUpdate) return NextResponse.json(result) } // 同步单个商品(通过 Payload ID) if (payloadId) { const result = await syncSingleProductByPayloadId( payload, payloadId, collection || '', forceUpdate, ) return NextResponse.json(result) } // 同步所有商品 const result = await syncAllProducts(payload, forceUpdate) return NextResponse.json(result) } catch (error) { console.error('Sync error:', error) return NextResponse.json( { success: false, error: error instanceof Error ? error.message : 'Unknown error', }, { status: 500 }, ) } } /** * 通过 Medusa ID 同步单个商品 */ async function syncSingleProductByMedusaId(payload: any, medusaId: string, forceUpdate: boolean) { try { // 从 Medusa 获取商品数据 const medusaProducts = await getAllMedusaProducts() const medusaProduct = medusaProducts.find((p) => p.id === medusaId) if (!medusaProduct) { return { success: false, action: 'not_found', message: `Medusa 中未找到商品 ${medusaId}`, } } // 确定应该同步到哪个 collection const targetCollection = getProductCollection(medusaProduct) const otherCollection = targetCollection === 'preorder-products' ? 'products' : 'preorder-products' // 在目标 collection 中检查是否已存在 const existingInTarget = await payload.find({ collection: targetCollection, where: { medusaId: { equals: medusaId }, }, limit: 1, }) // 在另一个 collection 中检查是否存在(产品类型可能改变) const existingInOther = await payload.find({ collection: otherCollection, where: { medusaId: { equals: medusaId }, }, limit: 1, }) const productData = transformMedusaProductToPayload(medusaProduct) // 如果在另一个 collection 中存在,需要删除并在正确的 collection 中创建 if (existingInOther.docs[0]) { await payload.delete({ collection: otherCollection, id: existingInOther.docs[0].id, }) const created = await payload.create({ collection: targetCollection, data: productData, }) return { success: true, action: 'moved', message: `商品 ${medusaId} 已从 ${otherCollection} 移动到 ${targetCollection}`, productId: created.id, collection: targetCollection, } } const existingProduct = existingInTarget.docs[0] // 如果存在且不强制更新,跳过 if (existingProduct && !forceUpdate) { return { success: true, action: 'skipped', message: `商品 ${medusaId} 已存在于 ${targetCollection}`, productId: existingProduct.id, collection: targetCollection, } } if (existingProduct) { // 更新现有商品 const updated = await payload.update({ collection: targetCollection, id: existingProduct.id, data: productData, }) return { success: true, action: 'updated', message: `商品 ${medusaId} 已更新于 ${targetCollection}`, productId: updated.id, collection: targetCollection, } } else { // 创建新商品 const created = await payload.create({ collection: targetCollection, data: productData, }) return { success: true, action: 'created', message: `商品 ${medusaId} 已创建于 ${targetCollection}`, productId: created.id, collection: targetCollection, } } } catch (error) { console.error(`Error syncing product ${medusaId}:`, error) return { success: false, action: 'error', message: `同步商品 ${medusaId} 失败`, error: error instanceof Error ? error.message : 'Unknown error', } } } /** * 通过 Payload ID 同步单个商品 */ async function syncSingleProductByPayloadId( payload: any, payloadId: string, collection: string, forceUpdate: boolean, ) { try { // 如果未指定 collection,尝试在两个 collections 中查找 let product: any = null let foundCollection: string = collection if (collection) { product = await payload.findByID({ collection, id: payloadId, }) } else { // 尝试 preorder-products try { product = await payload.findByID({ collection: 'preorder-products', id: payloadId, }) foundCollection = 'preorder-products' } catch { // 最后尝试旧的 products product = await payload.findByID({ collection: 'products', id: payloadId, }) foundCollection = 'products' } } if (!product) { return { success: false, action: 'not_found', message: `未找到商品 ID: ${payloadId}`, } } // 如果没有 medusaId,无法同步 if (!product.medusaId) { return { success: false, action: 'no_medusa_id', message: `商品 ${product.title} 没有关联的 Medusa ID,无法同步`, } } // 使用 medusaId 同步 return await syncSingleProductByMedusaId(payload, product.medusaId, forceUpdate) } catch (error) { console.error(`Error syncing product by Payload ID ${payloadId}:`, error) return { success: false, action: 'error', message: `同步商品 ID ${payloadId} 失败`, error: error instanceof Error ? error.message : 'Unknown error', } } } /** * 同步所有商品 */ async function syncAllProducts(payload: any, forceUpdate: boolean) { try { let offset = 0 const limit = 100 let hasMore = true const results = { total: 0, created: 0, updated: 0, skipped: 0, errors: 0, details: [] as any[], } while (hasMore) { // 分页获取 Medusa 商品 const { products: medusaProducts, count } = await getMedusaProductsPaginated(offset, limit) if (medusaProducts.length === 0) { hasMore = false break } results.total += medusaProducts.length // 处理每个商品 for (const medusaProduct of medusaProducts) { try { // 确定应该同步到哪个 collection const targetCollection = getProductCollection(medusaProduct) const otherCollection = targetCollection === 'preorder-products' ? 'products' : 'preorder-products' // 在目标 collection 中检查是否已存在 const existingInTarget = await payload.find({ collection: targetCollection, where: { medusaId: { equals: medusaProduct.id }, }, limit: 1, }) // 在另一个 collection 中检查是否存在(产品类型可能改变) const existingInOther = await payload.find({ collection: otherCollection, where: { medusaId: { equals: medusaProduct.id }, }, limit: 1, }) const productData = transformMedusaProductToPayload(medusaProduct) // 如果在错误的 collection 中,移动它 if (existingInOther.docs[0]) { await payload.delete({ collection: otherCollection, id: existingInOther.docs[0].id, }) await payload.create({ collection: targetCollection, data: productData, }) results.updated++ results.details.push({ medusaId: medusaProduct.id, title: medusaProduct.title, action: 'moved', from: otherCollection, to: targetCollection, }) continue } const existingProduct = existingInTarget.docs[0] // 如果存在且不强制更新,跳过 if (existingProduct && !forceUpdate) { results.skipped++ results.details.push({ medusaId: medusaProduct.id, title: medusaProduct.title, action: 'skipped', collection: targetCollection, }) continue } if (existingProduct) { // 更新 await payload.update({ collection: targetCollection, id: existingProduct.id, data: productData, }) results.updated++ results.details.push({ medusaId: medusaProduct.id, title: medusaProduct.title, action: 'updated', collection: targetCollection, }) } else { // 创建 await payload.create({ collection: targetCollection, data: productData, }) results.created++ results.details.push({ medusaId: medusaProduct.id, title: medusaProduct.title, action: 'created', collection: targetCollection, }) } } catch (error) { console.error(`Error processing product ${medusaProduct.id}:`, error) results.errors++ results.details.push({ medusaId: medusaProduct.id, title: medusaProduct.title, action: 'error', error: error instanceof Error ? error.message : 'Unknown error', }) } } // 更新偏移量 offset += limit if (offset >= count) { hasMore = false } } return { success: true, message: `同步完成: ${results.created} 个创建, ${results.updated} 个更新, ${results.skipped} 个跳过, ${results.errors} 个错误`, results, } } catch (error) { console.error('Error syncing all products:', error) throw error } } /** * POST /api/sync-medusa * 触发手动同步(需要认证) */ export async function POST(request: Request) { try { const payload = await getPayload({ config }) // 可以在这里添加认证检查 // const { user } = await payload.auth({ headers: request.headers }) // if (!user) { // return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) // } const body = await request.json() const { medusaId, payloadId, collection = '', forceUpdate = true } = body if (medusaId) { const result = await syncSingleProductByMedusaId(payload, medusaId, forceUpdate) return NextResponse.json(result) } if (payloadId) { const result = await syncSingleProductByPayloadId(payload, payloadId, collection, forceUpdate) return NextResponse.json(result) } const result = await syncAllProducts(payload, forceUpdate) return NextResponse.json(result) } catch (error) { console.error('Sync error:', error) return NextResponse.json( { success: false, error: error instanceof Error ? error.message : 'Unknown error', }, { status: 500 }, ) } }