gbmake-payload/src/app/api/sync-medusa/route.ts

434 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 },
)
}
}