208 lines
5.8 KiB
TypeScript
208 lines
5.8 KiB
TypeScript
import { NextRequest } from 'next/server'
|
|
import { prisma } from '@/lib/prisma'
|
|
import { rewriteStorageUrlForClient } from '@/lib/url-rewrite'
|
|
import {
|
|
API_ERROR,
|
|
errorResponse,
|
|
getApiKeyFromRequest,
|
|
getClientIp,
|
|
logApiCall,
|
|
successResponse,
|
|
validateApiKey,
|
|
} from '@/lib/external-api'
|
|
|
|
interface BarcodeImageItem {
|
|
materialId: string
|
|
assetId: string
|
|
taskNo: string | null
|
|
productModel: string
|
|
categoryName: string
|
|
shopName: string
|
|
sceneName: string
|
|
skuCode: string | null
|
|
barcodes: string[]
|
|
previewUrl: string
|
|
fileName: string
|
|
sortOrder: number
|
|
uploadedAt: string
|
|
updatedAt: string
|
|
uploaderName: string
|
|
isLatest: boolean
|
|
}
|
|
|
|
export async function GET(request: NextRequest) {
|
|
const startTime = Date.now()
|
|
const apiKey = getApiKeyFromRequest(request)
|
|
const rawBarcode = request.nextUrl.searchParams.get('barcode')
|
|
const barcode = String(rawBarcode || '').trim()
|
|
|
|
const keyRecord = await validateApiKey(apiKey)
|
|
if (!keyRecord) {
|
|
return errorResponse(API_ERROR.INVALID_API_KEY)
|
|
}
|
|
|
|
if (!barcode) {
|
|
await logApiCall({
|
|
apiKeyId: keyRecord.id,
|
|
endpoint: '/api/external/product-barcode-images',
|
|
method: 'GET',
|
|
queryType: 'barcode',
|
|
queryValue: rawBarcode || undefined,
|
|
statusCode: 400,
|
|
duration: Date.now() - startTime,
|
|
ipAddress: getClientIp(request),
|
|
userAgent: request.headers.get('user-agent') || undefined,
|
|
})
|
|
return errorResponse(API_ERROR.INVALID_BARCODE)
|
|
}
|
|
|
|
try {
|
|
const matches = await prisma.materialAssetBarcode.findMany({
|
|
where: {
|
|
barcode,
|
|
asset: {
|
|
assetType: 'sku-preview',
|
|
material: {
|
|
materialType: '任务-详情',
|
|
taskDetailPrivate: false,
|
|
deletedAt: null,
|
|
},
|
|
},
|
|
},
|
|
select: {
|
|
assetId: true,
|
|
barcode: true,
|
|
asset: {
|
|
select: {
|
|
id: true,
|
|
fileUrl: true,
|
|
fileName: true,
|
|
sortOrder: true,
|
|
skuCode: true,
|
|
createdAt: true,
|
|
updatedAt: true,
|
|
barcodes: {
|
|
select: {
|
|
barcode: true,
|
|
},
|
|
},
|
|
material: {
|
|
select: {
|
|
id: true,
|
|
taskNo: true,
|
|
productModel: true,
|
|
categoryName: true,
|
|
sceneName: true,
|
|
authorName: true,
|
|
user: {
|
|
select: {
|
|
realName: true,
|
|
username: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
})
|
|
|
|
const items = matches
|
|
.map((match): BarcodeImageItem | null => {
|
|
const asset = match.asset
|
|
const material = asset?.material
|
|
if (!asset || !material) {
|
|
return null
|
|
}
|
|
|
|
return {
|
|
materialId: String(material.id),
|
|
assetId: String(asset.id),
|
|
taskNo: material.taskNo || null,
|
|
productModel: String(material.productModel || ''),
|
|
categoryName: String(material.categoryName || ''),
|
|
shopName: String(material.categoryName || ''),
|
|
sceneName: String(material.sceneName || ''),
|
|
skuCode: asset.skuCode || null,
|
|
barcodes: Array.from(
|
|
new Set((asset.barcodes || []).map((item: any) => String(item.barcode || '').trim()).filter(Boolean)),
|
|
),
|
|
previewUrl: rewriteStorageUrlForClient(asset.fileUrl, request) || String(asset.fileUrl || ''),
|
|
fileName: String(asset.fileName || ''),
|
|
sortOrder: Number(asset.sortOrder || 0),
|
|
uploadedAt: asset.createdAt.toISOString(),
|
|
updatedAt: asset.updatedAt.toISOString(),
|
|
uploaderName:
|
|
material.user?.realName || material.user?.username || material.authorName || '',
|
|
isLatest: false,
|
|
}
|
|
})
|
|
.filter((item): item is BarcodeImageItem => Boolean(item))
|
|
.sort((left, right) => {
|
|
const uploadedAtDiff =
|
|
new Date(right.uploadedAt).getTime() - new Date(left.uploadedAt).getTime()
|
|
if (uploadedAtDiff !== 0) {
|
|
return uploadedAtDiff
|
|
}
|
|
|
|
return left.sortOrder - right.sortOrder
|
|
})
|
|
|
|
if (items.length === 0) {
|
|
await logApiCall({
|
|
apiKeyId: keyRecord.id,
|
|
endpoint: '/api/external/product-barcode-images',
|
|
method: 'GET',
|
|
queryType: 'barcode',
|
|
queryValue: barcode,
|
|
statusCode: 404,
|
|
duration: Date.now() - startTime,
|
|
ipAddress: getClientIp(request),
|
|
userAgent: request.headers.get('user-agent') || undefined,
|
|
})
|
|
return errorResponse(API_ERROR.BARCODE_NOT_FOUND)
|
|
}
|
|
|
|
const latestAssetId = items[0].assetId
|
|
const responseData = {
|
|
barcode,
|
|
total: items.length,
|
|
items: items.map((item) => ({
|
|
...item,
|
|
isLatest: item.assetId === latestAssetId,
|
|
})),
|
|
}
|
|
|
|
await logApiCall({
|
|
apiKeyId: keyRecord.id,
|
|
endpoint: '/api/external/product-barcode-images',
|
|
method: 'GET',
|
|
taskNo: responseData.items[0]?.taskNo || undefined,
|
|
queryType: 'barcode',
|
|
queryValue: barcode,
|
|
statusCode: 200,
|
|
duration: Date.now() - startTime,
|
|
ipAddress: getClientIp(request),
|
|
userAgent: request.headers.get('user-agent') || undefined,
|
|
})
|
|
|
|
return successResponse(responseData)
|
|
} catch (error) {
|
|
console.error('[external/product-barcode-images] GET error:', error)
|
|
|
|
await logApiCall({
|
|
apiKeyId: keyRecord.id,
|
|
endpoint: '/api/external/product-barcode-images',
|
|
method: 'GET',
|
|
queryType: 'barcode',
|
|
queryValue: barcode,
|
|
statusCode: 500,
|
|
duration: Date.now() - startTime,
|
|
ipAddress: getClientIp(request),
|
|
userAgent: request.headers.get('user-agent') || undefined,
|
|
})
|
|
|
|
return errorResponse(API_ERROR.SERVER_ERROR)
|
|
}
|
|
}
|