diff --git a/src/app/api/sync-medusa/route.ts b/src/app/api/sync-medusa/route.ts index fb8fbba..84dae42 100644 --- a/src/app/api/sync-medusa/route.ts +++ b/src/app/api/sync-medusa/route.ts @@ -126,14 +126,14 @@ export async function GET(request: Request) { const { searchParams } = new URL(request.url) const medusaId = searchParams.get('medusaId') const payloadId = searchParams.get('payloadId') - const collection = searchParams.get('collection') + const collection = searchParams.get('collection') as 'products' | 'preorder-products' | null const forceUpdate = searchParams.get('forceUpdate') === 'true' const payload = await getPayload({ config }) // 同步单个商品(通过 Medusa ID) if (medusaId) { - const result = await syncSingleProductByMedusaId(payload, medusaId, forceUpdate) + const result = await syncSingleProductByMedusaId(payload, medusaId, forceUpdate, collection || undefined) return NextResponse.json(result) } @@ -166,13 +166,22 @@ export async function GET(request: Request) { /** * 通过 Medusa ID 同步单个商品 */ -async function syncSingleProductByMedusaId(payload: any, medusaId: string, forceUpdate: boolean) { +async function syncSingleProductByMedusaId( + payload: any, + medusaId: string, + forceUpdate: boolean, + preferredCollection?: 'products' | 'preorder-products' +) { + console.log(`[Sync API] 🔄 开始同步产品: ${medusaId}`) + console.log(`[Sync API] ⚙️ forceUpdate: ${forceUpdate}, preferredCollection: ${preferredCollection || 'auto'}`) + try { // 从 Medusa 获取商品数据 const medusaProducts = await getAllMedusaProducts() const medusaProduct = medusaProducts.find((p) => p.id === medusaId) if (!medusaProduct) { + console.error(`[Sync API] ❌ Medusa 中未找到商品: ${medusaId}`) return { success: false, action: 'not_found', @@ -180,20 +189,27 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force } } - // 确定应该同步到哪个 collection - const targetCollection = getProductCollection(medusaProduct) + console.log(`[Sync API] ✅ 找到 Medusa 产品: ${medusaProduct.title}`) + + // 确定应该同步到哪个 collection(优先使用传入的 collection,否则自动判断) + const targetCollection = preferredCollection || getProductCollection(medusaProduct) + console.log(`[Sync API] 🎯 目标 collection: ${targetCollection}${preferredCollection ? ' (指定)' : ' (自动判断)'}`) + const otherCollection = targetCollection === 'preorder-products' ? 'products' : 'preorder-products' // 转换数据 const productData = transformMedusaProductToPayload(medusaProduct) const seedId = productData.seedId + console.log(`[Sync API] 📦 产品数据: title=${productData.title}, seedId=${seedId}`) // 使用新的查找函数(优先 seedId) const found = await findProductBySeedIdOrMedusaId(payload, seedId, medusaId) // 如果在另一个 collection 中找到,需要移动 if (found && found.collection !== targetCollection) { + console.log(`[Sync API] 🚚 需要移动: ${found.collection} -> ${targetCollection}`) + await payload.delete({ collection: found.collection, id: found.product.id, @@ -204,6 +220,7 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force data: productData, }) + console.log(`[Sync API] ✅ 移动成功, 新 ID: ${created.id}`) return { success: true, action: 'moved', @@ -215,16 +232,19 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force // 如果在目标 collection 中找到 if (found) { + console.log(`[Sync API] 🔎 在 ${found.collection} 中找到现有产品, ID: ${found.product.id}`) const existingProduct = found.product // 如果存在且不强制更新,只更新空字段 if (!forceUpdate) { - // 合并数据(只更新空字段) + console.log(`[Sync API] 🔄 模式: 只填充空字段`) + // 合并数据(只曹新空字段) const mergedData = mergeProductData(existingProduct, productData, false) // 如果没有需要更新的字段,跳过 if (Object.keys(mergedData).length <= 2) { // 只有 lastSyncedAt 和 medusaId + console.log(`[Sync API] ⏭️ 跳过: 所有字段都有值`) return { success: true, action: 'skipped', @@ -234,6 +254,7 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force } } + console.log(`[Sync API] 📝 更新字段: ${Object.keys(mergedData).join(', ')}`) // 更新(只更新空字段) const updated = await payload.update({ collection: targetCollection, @@ -241,6 +262,7 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force data: mergedData, }) + console.log(`[Sync API] ✅ 部分更新成功`) return { success: true, action: 'updated_partial', @@ -250,6 +272,7 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force } } + console.log(`[Sync API] ⚡ 模式: 强制更新所有字段`) // 强制更新所有字段 const updated = await payload.update({ collection: targetCollection, @@ -257,6 +280,7 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force data: productData, }) + console.log(`[Sync API] ✅ 强制更新成功`) return { success: true, action: 'updated', @@ -266,12 +290,14 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force } } + console.log(`[Sync API] ✨ 创建新产品`) // 不存在,创建新商品 const created = await payload.create({ collection: targetCollection, data: productData, }) + console.log(`[Sync API] ✅ 创建成功, ID: ${created.id}`) return { success: true, action: 'created', @@ -280,7 +306,7 @@ async function syncSingleProductByMedusaId(payload: any, medusaId: string, force collection: targetCollection, } } catch (error) { - console.error(`Error syncing product ${medusaId}:`, error) + console.error(`[Sync API] ❌ 同步失败:`, error) return { success: false, action: 'error', @@ -344,8 +370,8 @@ async function syncSingleProductByPayloadId( } } - // 使用 medusaId 同步 - return await syncSingleProductByMedusaId(payload, product.medusaId, forceUpdate) + // 使用 medusaId 同步(保持在当前 collection) + return await syncSingleProductByMedusaId(payload, product.medusaId, forceUpdate, foundCollection as 'products' | 'preorder-products') } catch (error) { console.error(`Error syncing product by Payload ID ${payloadId}:`, error) return { @@ -545,7 +571,7 @@ export async function POST(request: Request) { const { medusaId, payloadId, collection = '', forceUpdate = true } = body if (medusaId) { - const result = await syncSingleProductByMedusaId(payload, medusaId, forceUpdate) + const result = await syncSingleProductByMedusaId(payload, medusaId, forceUpdate, collection || undefined) return NextResponse.json(result) } diff --git a/src/collections/PreorderProducts.ts b/src/collections/PreorderProducts.ts index 3fc2cbc..e90e48c 100644 --- a/src/collections/PreorderProducts.ts +++ b/src/collections/PreorderProducts.ts @@ -1,6 +1,7 @@ import type { CollectionConfig } from 'payload' import { logAfterChange, logAfterDelete } from '../hooks/logAction' import { cacheAfterChange, cacheAfterDelete } from '../hooks/cacheInvalidation' +import { ProductBaseFields, RelatedProductsField, TaobaoLinksField, MedusaAttributesTab } from './base/ProductBase' import { AlignFeature, BlocksFeature, @@ -53,78 +54,7 @@ export const PreorderProducts: CollectionConfig = { { label: 'ℹ️ 基本信息', fields: [ - { - type: 'row', - fields: [ - { - name: 'medusaId', - type: 'text', - required: true, - unique: true, - index: true, - admin: { - description: 'Medusa 商品 ID', - readOnly: true, - width: '60%', - }, - }, - { - name: 'status', - type: 'select', - required: true, - defaultValue: 'draft', - options: [ - { label: '草稿', value: 'draft' }, - { label: '已发布', value: 'published' }, - ], - admin: { - description: '商品详情状态', - width: '40%', - }, - }, - ], - }, - { - name: 'seedId', - type: 'text', - unique: true, - index: true, - admin: { - description: 'Seed ID (从 Medusa 同步,用于数据绑定)', - readOnly: true, - }, - }, - { - name: 'title', - type: 'text', - required: true, - admin: { - description: '商品标题(从 Medusa 同步)', - readOnly: true, - }, - }, - { - name: 'thumbnail', - type: 'text', - admin: { - description: '商品封面 URL(支持上传或输入 URL)', - components: { - Cell: '/components/cells/ThumbnailCell#ThumbnailCell', - Field: '/components/fields/ThumbnailField#ThumbnailField', - }, - }, - }, - { - name: 'lastSyncedAt', - type: 'date', - admin: { - description: '上次同步时间', - readOnly: true, - date: { - displayFormat: 'yyyy-MM-dd HH:mm:ss', - }, - }, - }, + ...ProductBaseFields, { name: 'progress', type: 'ui', @@ -170,14 +100,31 @@ export const PreorderProducts: CollectionConfig = { ], }, { - name: 'preorderEndDate', - type: 'date', - admin: { - description: '预购结束日期(可选)', - date: { - displayFormat: 'yyyy-MM-dd HH:mm', + type: 'row', + fields: [ + { + name: 'preorderStartDate', + type: 'date', + admin: { + description: '预购开始日期(可选)', + width: '50%', + date: { + displayFormat: 'yyyy-MM-dd HH:mm', + }, + }, }, - }, + { + name: 'preorderEndDate', + type: 'date', + admin: { + description: '预购结束日期(可选)', + width: '50%', + date: { + displayFormat: 'yyyy-MM-dd HH:mm', + }, + }, + }, + ], }, { type: 'ui', @@ -276,31 +223,7 @@ export const PreorderProducts: CollectionConfig = { }, { label: '🔗 相关商品', - fields: [ - { - name: 'relatedProducts', - type: 'relationship', - relationTo: ['preorder-products', 'products'], - hasMany: true, - admin: { - description: '推荐的相关商品', - components: { - Field: '/components/fields/RelatedProductsField#RelatedProductsField', - }, - }, - filterOptions: ({ relationTo, data }) => { - // 过滤掉当前商品本身,避免自引用 - if (data?.id) { - return { - id: { - not_equals: data.id, - }, - } - } - return true - }, - }, - ], + fields: [RelatedProductsField], }, { label: '📦 订单信息', @@ -316,69 +239,10 @@ export const PreorderProducts: CollectionConfig = { }, ], }, + MedusaAttributesTab, { label: '🛒 淘宝链接', - fields: [ - { - name: 'taobaoLinks', - type: 'array', - label: '淘宝采购链接列表', - admin: { - description: '💡 管理淘宝采购链接(仅后台显示,不通过 API 暴露)', - initCollapsed: false, - }, - access: { - read: ({ req: { user } }) => !!user, - update: ({ req: { user } }) => !!user, - }, - fields: [ - { - name: 'url', - type: 'text', - label: '🔗 淘宝链接', - required: true, - admin: { - placeholder: 'https://item.taobao.com/...', - }, - }, - { - name: 'title', - type: 'text', - label: '📝 标题', - admin: { - placeholder: '链接标题或商品名称', - }, - }, - { - name: 'thumbnail', - type: 'text', - label: '🖼️ 缩略图 URL', - admin: { - placeholder: 'https://...', - description: '淘宝商品图片地址', - }, - }, - { - name: 'note', - type: 'textarea', - label: '📄 备注', - admin: { - placeholder: '其他备注信息...', - rows: 3, - }, - }, - { - type: 'ui', - name: 'linkPreview', - admin: { - components: { - Field: '/components/fields/TaobaoLinkPreview#TaobaoLinkPreview', - }, - }, - }, - ], - }, - ], + fields: [TaobaoLinksField], }, ], }, diff --git a/src/collections/Products.ts b/src/collections/Products.ts index 1d80e98..32056ca 100644 --- a/src/collections/Products.ts +++ b/src/collections/Products.ts @@ -1,26 +1,15 @@ import type { CollectionConfig } from 'payload' import { logAfterChange, logAfterDelete } from '../hooks/logAction' import { cacheAfterChange, cacheAfterDelete } from '../hooks/cacheInvalidation' +import { ProductBaseFields, RelatedProductsField, TaobaoLinksField, MedusaAttributesTab } from './base/ProductBase' import { - AlignFeature, - BlocksFeature, - BoldFeature, - ChecklistFeature, HeadingFeature, - IndentFeature, - InlineCodeFeature, - ItalicFeature, lexicalEditor, LinkFeature, - OrderedListFeature, - ParagraphFeature, - RelationshipFeature, - UnorderedListFeature, UploadFeature, FixedToolbarFeature, InlineToolbarFeature, HorizontalRuleFeature, - BlockquoteFeature, } from '@payloadcms/richtext-lexical' export const Products: CollectionConfig = { @@ -49,86 +38,7 @@ export const Products: CollectionConfig = { tabs: [ { label: 'ℹ️ 基本信息', - fields: [ - { - type: 'row', - fields: [ - { - name: 'medusaId', - type: 'text', - required: true, - unique: true, - index: true, - admin: { - description: 'Medusa 商品 ID', - readOnly: true, - width: '60%', - }, - }, - { - name: 'status', - type: 'select', - required: true, - defaultValue: 'draft', - options: [ - { - label: '草稿', - value: 'draft', - }, - { - label: '已发布', - value: 'published', - }, - ], - admin: { - description: '商品详情状态', - width: '40%', - }, - }, - ], - }, - { - name: 'seedId', - type: 'text', - unique: true, - index: true, - admin: { - description: 'Seed ID (恥从 Medusa 同步,用于数据绑定)', - readOnly: true, - }, - }, - { - name: 'title', - type: 'text', - required: true, - admin: { - description: '商品标题(从 Medusa 同步)', - readOnly: true, - }, - }, - { - name: 'thumbnail', - type: 'text', - admin: { - description: '商品封面 URL(支持上传或输入 URL)', - components: { - Cell: '/components/cells/ThumbnailCell#ThumbnailCell', - Field: '/components/fields/ThumbnailField#ThumbnailField', - }, - }, - }, - { - name: 'lastSyncedAt', - type: 'date', - admin: { - description: '最后同步时间', - readOnly: true, - date: { - displayFormat: 'yyyy-MM-dd HH:mm:ss', - }, - }, - }, - ], + fields: ProductBaseFields, }, { label: '📄 商品详情', @@ -184,95 +94,12 @@ export const Products: CollectionConfig = { }, { label: '🔗 关联信息', - fields: [ - { - name: 'relatedProducts', - type: 'relationship', - relationTo: ['products', 'preorder-products'], - hasMany: true, - admin: { - description: '相关商品,支持搜索联想', - components: { - Field: '/components/fields/RelatedProductsField#RelatedProductsField', - }, - }, - filterOptions: ({ relationTo, data }) => { - // 过滤掉当前商品本身,避免自引用 - if (data?.id) { - return { - id: { - not_equals: data.id, - }, - } - } - return true - }, - }, - ], + fields: [RelatedProductsField], }, + MedusaAttributesTab, { label: '🛒 淘宝链接', - fields: [ - { - name: 'taobaoLinks', - type: 'array', - label: '淘宝采购链接列表', - admin: { - description: '💡 管理淘宝采购链接(仅后台显示,不通过 API 暴露)', - initCollapsed: false, - }, - access: { - read: ({ req: { user } }) => !!user, - update: ({ req: { user } }) => !!user, - }, - fields: [ - { - name: 'url', - type: 'text', - label: '🔗 淘宝链接', - required: true, - admin: { - placeholder: 'https://item.taobao.com/...', - }, - }, - { - name: 'title', - type: 'text', - label: '📝 标题', - admin: { - placeholder: '链接标题或商品名称', - }, - }, - { - name: 'thumbnail', - type: 'text', - label: '🖼️ 缩略图 URL', - admin: { - placeholder: 'https://...', - description: '淘宝商品图片地址', - }, - }, - { - name: 'note', - type: 'textarea', - label: '📄 备注', - admin: { - placeholder: '其他备注信息...', - rows: 3, - }, - }, - { - type: 'ui', - name: 'linkPreview', - admin: { - components: { - Field: '/components/fields/TaobaoLinkPreview#TaobaoLinkPreview', - }, - }, - }, - ], - }, - ], + fields: [TaobaoLinksField], }, ], }, diff --git a/src/collections/base/ProductBase.ts b/src/collections/base/ProductBase.ts new file mode 100644 index 0000000..b0ae6c5 --- /dev/null +++ b/src/collections/base/ProductBase.ts @@ -0,0 +1,311 @@ +import type { Field, Tab } from 'payload' +import { + HeadingFeature, + lexicalEditor, + LinkFeature, + UploadFeature, + FixedToolbarFeature, + InlineToolbarFeature, + HorizontalRuleFeature, +} from '@payloadcms/richtext-lexical' + +/** + * 产品集合的共同字段基类 + * 用于 Products 和 PreorderProducts 集合 + */ +export const ProductBaseFields: Field[] = [ + // ========== 基本信息字段 ========== + { + type: 'row', + fields: [ + { + name: 'medusaId', + type: 'text', + required: true, + unique: true, + index: true, + admin: { + description: 'Medusa 商品 ID', + readOnly: true, + width: '60%', + }, + }, + { + name: 'status', + type: 'select', + required: true, + defaultValue: 'draft', + options: [ + { label: '草稿', value: 'draft' }, + { label: '已发布', value: 'published' }, + ], + admin: { + description: '商品详情状态', + width: '40%', + }, + }, + ], + }, + { + name: 'seedId', + type: 'text', + unique: true, + index: true, + admin: { + description: 'Seed ID (从 Medusa 同步,用于数据绑定)', + readOnly: true, + }, + }, + { + name: 'title', + type: 'text', + required: true, + admin: { + description: '商品标题(从 Medusa 同步)', + readOnly: true, + }, + }, + { + name: 'thumbnail', + type: 'text', + admin: { + description: '商品封面 URL(支持上传或输入 URL)', + components: { + Cell: '/components/cells/ThumbnailCell#ThumbnailCell', + Field: '/components/fields/ThumbnailField#ThumbnailField', + }, + }, + }, + { + name: 'lastSyncedAt', + type: 'date', + admin: { + description: '最后同步时间', + readOnly: true, + date: { + displayFormat: 'yyyy-MM-dd HH:mm:ss', + }, + }, + }, +] + +/** + * Medusa 默认属性字段 + * 对应 Medusa 中的标签、类型、系列、分类、物理属性等 + */ +export const MedusaAttributesFields: Field[] = [ + { + name: 'tags', + type: 'text', + admin: { + description: '产品标签(逗号分隔)', + placeholder: '例如: 热门, 新品, 限量', + }, + }, + { + name: 'type', + type: 'text', + admin: { + description: '产品类型', + placeholder: '例如: 外壳, PCB, 工具', + }, + }, + { + name: 'collection', + type: 'text', + admin: { + description: '产品系列', + placeholder: '例如: Shell, Cartridge', + }, + }, + { + name: 'category', + type: 'text', + admin: { + description: '产品分类', + placeholder: '例如: GBA, GBC, GB', + }, + }, + { + type: 'collapsible', + label: '物理属性', + fields: [ + { + type: 'row', + fields: [ + { + name: 'height', + type: 'number', + admin: { + description: '高度 (cm)', + width: '25%', + }, + }, + { + name: 'width', + type: 'number', + admin: { + description: '宽度 (cm)', + width: '25%', + }, + }, + { + name: 'length', + type: 'number', + admin: { + description: '长度 (cm)', + width: '25%', + }, + }, + { + name: 'weight', + type: 'number', + admin: { + description: '重量 (g)', + width: '25%', + }, + }, + ], + }, + ], + }, + { + type: 'collapsible', + label: '海关与物流', + fields: [ + { + type: 'row', + fields: [ + { + name: 'midCode', + type: 'text', + admin: { + description: 'MID 代码(制造商识别码)', + placeholder: '例如: 1234567890', + width: '33%', + }, + }, + { + name: 'hsCode', + type: 'text', + admin: { + description: 'HS 代码(海关编码)', + placeholder: '例如: 8523.49.00', + width: '33%', + }, + }, + { + name: 'countryOfOrigin', + type: 'text', + admin: { + description: '原产国', + placeholder: '例如: CN, US, JP', + width: '34%', + }, + }, + ], + }, + ], + }, +] + +/** + * Medusa 属性 Tab + * 包含所有 Medusa 默认的产品属性字段 + */ +export const MedusaAttributesTab: Tab = { + label: '🏷️ 属性', + fields: MedusaAttributesFields, +} + +/** + * 相关产品字段配置 + * 支持跨集合关联 (products 和 preorder-products) + */ +export const RelatedProductsField: Field = { + name: 'relatedProducts', + type: 'relationship', + relationTo: ['products', 'preorder-products'], + hasMany: true, + admin: { + description: '相关商品,支持搜索联想', + components: { + Field: '/components/fields/RelatedProductsField#RelatedProductsField', + }, + }, + filterOptions: ({ relationTo, data }) => { + // 过滤掉当前商品本身,避免自引用 + if (data?.id) { + return { + id: { + not_equals: data.id, + }, + } + } + return true + }, +} + +/** + * 淘宝链接字段配置 + * 仅后台管理员可见,不通过 API 暴露 + */ +export const TaobaoLinksField: Field = { + name: 'taobaoLinks', + type: 'array', + label: '淘宝采购链接列表', + admin: { + description: '💡 管理淘宝采购链接(仅后台显示,不通过 API 暴露)', + initCollapsed: false, + }, + access: { + read: ({ req: { user } }) => !!user, + update: ({ req: { user } }) => !!user, + }, + fields: [ + { + name: 'url', + type: 'text', + label: '🔗 淘宝链接', + required: true, + admin: { + placeholder: 'https://item.taobao.com/...', + }, + }, + { + name: 'title', + type: 'text', + label: '📝 标题', + admin: { + placeholder: '链接标题或商品名称', + }, + }, + { + name: 'thumbnail', + type: 'text', + label: '🖼️ 缩略图 URL', + admin: { + placeholder: 'https://...', + description: '淘宝商品图片地址', + }, + }, + { + name: 'note', + type: 'textarea', + label: '📄 备注', + admin: { + placeholder: '其他备注信息...', + rows: 3, + }, + }, + { + type: 'ui', + name: 'linkPreview', + admin: { + components: { + Field: '/components/fields/TaobaoLinkPreview#TaobaoLinkPreview', + }, + }, + }, + ], +} diff --git a/src/components/cells/ThumbnailCell.tsx b/src/components/cells/ThumbnailCell.tsx index cd1554d..47b05d9 100644 --- a/src/components/cells/ThumbnailCell.tsx +++ b/src/components/cells/ThumbnailCell.tsx @@ -1,19 +1,21 @@ 'use client' import Link from 'next/link' -import { usePathname } from 'next/navigation' export const ThumbnailCell = (props: any) => { - const pathname = usePathname() - - // 从 URL 路径中提取 collection slug - const collectionSlug = pathname?.includes('/preorder-products') - ? 'preorder-products' - : 'products' - // 尝试从不同的 props 路径获取值 const value = props.value || props.cellData || props.data const rowData = props.rowData || props.row + // 优先从 props 中获取 collection 信息(Payload Cell API) + let collectionSlug = props.collectionConfig?.slug || props.field?.relationTo || props.collection + + // 如果没有从 props 获取到,通过检查预购特有字段自动判断 + if (!collectionSlug) { + // PreorderProducts 有 orderCount, preorderType 等特有字段 + const isPreorder = rowData?.orderCount !== undefined || rowData?.preorderType !== undefined + collectionSlug = isPreorder ? 'preorder-products' : 'products' + } + const isImage = typeof value === 'string' && value.match(/^https?:\/\/.+/) const editUrl = `/admin/collections/${collectionSlug}/${rowData?.id || ''}` diff --git a/src/lib/medusa.ts b/src/lib/medusa.ts index f9bd895..6f1c92b 100644 --- a/src/lib/medusa.ts +++ b/src/lib/medusa.ts @@ -211,6 +211,9 @@ export function transformMedusaProductToPayload(product: MedusaProduct) { thumbnailUrl = product.images[0].url } + // 提取 tags(逗号分隔) + const tags = product.tags?.map(tag => tag.value).join(', ') || null + return { medusaId: product.id, seedId: product.metadata?.seed_id || null, @@ -219,6 +222,23 @@ export function transformMedusaProductToPayload(product: MedusaProduct) { thumbnail: thumbnailUrl || null, status: 'draft', lastSyncedAt: new Date().toISOString(), + + // Medusa 默认属性 + tags: tags, + type: product.type_id || null, + collection: product.collection_id || null, + category: product.metadata?.category || null, + + // 物理属性(从 metadata 中提取) + height: product.metadata?.height ? Number(product.metadata.height) : null, + width: product.metadata?.width ? Number(product.metadata.width) : null, + length: product.metadata?.length ? Number(product.metadata.length) : null, + weight: product.metadata?.weight ? Number(product.metadata.weight) : null, + + // 海关与物流 + midCode: product.metadata?.mid_code || product.metadata?.midCode || null, + hsCode: product.metadata?.hs_code || product.metadata?.hsCode || null, + countryOfOrigin: product.metadata?.country_of_origin || product.metadata?.countryOfOrigin || null, } } diff --git a/src/payload-types.ts b/src/payload-types.ts index 5b52bd9..3c49fb3 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -206,7 +206,7 @@ export interface Product { */ status: 'draft' | 'published'; /** - * Seed ID (恥从 Medusa 同步,用于数据绑定) + * Seed ID (从 Medusa 同步,用于数据绑定) */ seedId?: string | null; /** @@ -254,6 +254,50 @@ export interface Product { } )[] | null; + /** + * 产品标签(逗号分隔) + */ + tags?: string | null; + /** + * 产品类型 + */ + type?: string | null; + /** + * 产品系列 + */ + collection?: string | null; + /** + * 产品分类 + */ + category?: string | null; + /** + * 高度 (cm) + */ + height?: number | null; + /** + * 宽度 (cm) + */ + width?: number | null; + /** + * 长度 (cm) + */ + length?: number | null; + /** + * 重量 (g) + */ + weight?: number | null; + /** + * MID 代码(制造商识别码) + */ + midCode?: string | null; + /** + * HS 代码(海关编码) + */ + hsCode?: string | null; + /** + * 原产国 + */ + countryOfOrigin?: string | null; /** * 💡 管理淘宝采购链接(仅后台显示,不通过 API 暴露) */ @@ -301,7 +345,7 @@ export interface PreorderProduct { */ thumbnail?: string | null; /** - * 上次同步时间 + * 最后同步时间 */ lastSyncedAt?: string | null; /** @@ -312,6 +356,10 @@ export interface PreorderProduct { * 众筹目标数量(0 表示以变体 max_orders 总和为准) */ fundingGoal: number; + /** + * 预购开始日期(可选) + */ + preorderStartDate?: string | null; /** * 预购结束日期(可选) */ @@ -343,20 +391,64 @@ export interface PreorderProduct { [k: string]: unknown; } | null; /** - * 推荐的相关商品 + * 相关商品,支持搜索联想 */ relatedProducts?: | ( - | { - relationTo: 'preorder-products'; - value: number | PreorderProduct; - } | { relationTo: 'products'; value: number | Product; } + | { + relationTo: 'preorder-products'; + value: number | PreorderProduct; + } )[] | null; + /** + * 产品标签(逗号分隔) + */ + tags?: string | null; + /** + * 产品类型 + */ + type?: string | null; + /** + * 产品系列 + */ + collection?: string | null; + /** + * 产品分类 + */ + category?: string | null; + /** + * 高度 (cm) + */ + height?: number | null; + /** + * 宽度 (cm) + */ + width?: number | null; + /** + * 长度 (cm) + */ + length?: number | null; + /** + * 重量 (g) + */ + weight?: number | null; + /** + * MID 代码(制造商识别码) + */ + midCode?: string | null; + /** + * HS 代码(海关编码) + */ + hsCode?: string | null; + /** + * 原产国 + */ + countryOfOrigin?: string | null; /** * 💡 管理淘宝采购链接(仅后台显示,不通过 API 暴露) */ @@ -821,6 +913,17 @@ export interface ProductsSelect { lastSyncedAt?: T; content?: T; relatedProducts?: T; + tags?: T; + type?: T; + collection?: T; + category?: T; + height?: T; + width?: T; + length?: T; + weight?: T; + midCode?: T; + hsCode?: T; + countryOfOrigin?: T; taobaoLinks?: | T | { @@ -846,11 +949,23 @@ export interface PreorderProductsSelect { lastSyncedAt?: T; preorderType?: T; fundingGoal?: T; + preorderStartDate?: T; preorderEndDate?: T; orderCount?: T; fakeOrderCount?: T; description?: T; relatedProducts?: T; + tags?: T; + type?: T; + collection?: T; + category?: T; + height?: T; + width?: T; + length?: T; + weight?: T; + midCode?: T; + hsCode?: T; + countryOfOrigin?: T; taobaoLinks?: | T | {