feat(edit): 编辑上报细节优化
This commit is contained in:
@@ -2,6 +2,8 @@ import type {
|
||||
CreateMediaUploadIntentRequest,
|
||||
MediaAccessUrl,
|
||||
MediaAsset,
|
||||
DeleteProductImageRequest,
|
||||
ProductImageDeletion,
|
||||
MediaUploadIntent,
|
||||
ProductUploadImage,
|
||||
ProductImportedImage,
|
||||
@@ -21,6 +23,13 @@ export const WATERMARK_IMAGE_MAX_BYTES = 5 * 1024 * 1024;
|
||||
|
||||
/** 异步操作查询,契约见 docs/api/conventions.md §5(GET /api/operations/{operation_id}) */
|
||||
export const mediaApi = {
|
||||
listPendingImageDeletions: (productId: string) => api.get<ProductImageDeletion[]>(`/products/${productId}/image-deletions`),
|
||||
deleteProductImage: (productId: string, payload: DeleteProductImageRequest) =>
|
||||
api.post<ProductImageDeletion>(`/products/${productId}/image-deletions`, payload,
|
||||
{ headers: { 'Idempotency-Key': crypto.randomUUID() } }),
|
||||
readImageDeletion: (productId: string, id: string) => api.get<ProductImageDeletion>(`/products/${productId}/image-deletions/${id}`),
|
||||
retryImageDeletion: (productId: string, id: string) => api.post<ProductImageDeletion>(`/products/${productId}/image-deletions/${id}/retry`, {},
|
||||
{ headers: { 'Idempotency-Key': crypto.randomUUID() } }),
|
||||
createAiEdit: (assetId: string, payload: CreateMediaAiEditRequest) =>
|
||||
api.post<MediaAiEdit>(`/media-assets/${assetId}/ai-edits`, payload,
|
||||
{ headers: { 'Idempotency-Key': crypto.randomUUID() } }),
|
||||
@@ -47,8 +56,8 @@ export const mediaApi = {
|
||||
{ headers: { 'Idempotency-Key': crypto.randomUUID() } }),
|
||||
readWatermarkDerivative: (derivativeId: string) =>
|
||||
api.get<WatermarkDerivative>(`/media-watermark-derivatives/${derivativeId}`),
|
||||
listProductUploads: () => api.get<ProductUploadImage[]>('/media-assets/product-uploads'),
|
||||
listProductImports: () => api.get<ProductImportedImage[]>('/media-assets/product-imports'),
|
||||
listProductUploads: (productId: string) => api.get<ProductUploadImage[]>(`/media-assets/product-uploads?product_id=${encodeURIComponent(productId)}`),
|
||||
listProductImports: (productId: string) => api.get<ProductImportedImage[]>(`/media-assets/product-imports?product_id=${encodeURIComponent(productId)}`),
|
||||
importProductImage: (payload: ImportProductImageRequest) =>
|
||||
api.post<ProductImportedImage>('/media-assets/product-imports', payload, {
|
||||
headers: { 'Idempotency-Key': crypto.randomUUID() },
|
||||
@@ -165,17 +174,19 @@ export async function uploadWatermarkImage(file: File): Promise<MediaAsset> {
|
||||
}
|
||||
|
||||
/** 商品图片沿用素材直传协议,完成服务端校验后才可写入 SKU 草稿。 */
|
||||
export async function uploadProductImage(file: File): Promise<MediaAsset> {
|
||||
return uploadImage(file, 'product_image');
|
||||
export async function uploadProductImage(file: File, productId: string): Promise<MediaAsset> {
|
||||
return uploadImage(file, 'product_image', productId);
|
||||
}
|
||||
|
||||
async function uploadImage(
|
||||
file: File,
|
||||
purpose: CreateMediaUploadIntentRequest['purpose']
|
||||
purpose: CreateMediaUploadIntentRequest['purpose'],
|
||||
productId?: string
|
||||
): Promise<MediaAsset> {
|
||||
const checksumSha256 = await sha256Hex(file);
|
||||
const intent = await mediaApi.createUploadIntent({
|
||||
purpose,
|
||||
...(productId ? { product_id: productId } : {}),
|
||||
filename: file.name,
|
||||
content_type: file.type as CreateMediaUploadIntentRequest['content_type'],
|
||||
byte_size: file.size,
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
ProductEditorContext,
|
||||
ProductEditorSnapshot,
|
||||
ProductAiTask,
|
||||
RequestProductAiTask,
|
||||
ProductEditorValidation,
|
||||
ProductPublishPreflight,
|
||||
ProductPublishJob,
|
||||
@@ -29,10 +30,21 @@ export const productEditorApi = {
|
||||
},
|
||||
}
|
||||
),
|
||||
read: (productId: string, platformId: string) =>
|
||||
api.get<ProductEditorSnapshot>(`/products/${productId}/editor`, {
|
||||
read: async (
|
||||
productId: string,
|
||||
platformId: string,
|
||||
collectionItemId?: string | null
|
||||
): Promise<ProductEditorSnapshot> => {
|
||||
// 插件首次进入时尚无平台配置,沿用采集箱的初始化流程并校验商品归属。
|
||||
if (collectionItemId) {
|
||||
const context = await productEditorApi.enter(collectionItemId, platformId);
|
||||
if (context.product_id !== productId)
|
||||
throw new Error('采集记录与商品不匹配,请从采集箱重新进入编辑页');
|
||||
}
|
||||
return api.get<ProductEditorSnapshot>(`/products/${productId}/editor`, {
|
||||
headers: { 'X-Atlas-Platform-Id': platformId },
|
||||
}),
|
||||
});
|
||||
},
|
||||
aiAutofill: (profileId: string, platformId: string) =>
|
||||
api.post<{ ai_task_id: string; status: 'queued' }>(
|
||||
`/product-platform-profiles/${profileId}/ai/autofills`,
|
||||
@@ -44,10 +56,10 @@ export const productEditorApi = {
|
||||
},
|
||||
}
|
||||
),
|
||||
aiSkuCopy: (profileId: string, variantId: string, platformId: string) =>
|
||||
aiSkuCopy: (profileId: string, variantId: string, platformId: string, input: RequestProductAiTask = {}) =>
|
||||
api.post<{ ai_task_id: string; status: 'queued' }>(
|
||||
`/product-platform-profiles/${profileId}/variants/${variantId}/ai/copy`,
|
||||
{},
|
||||
input,
|
||||
{
|
||||
headers: {
|
||||
'X-Atlas-Platform-Id': platformId,
|
||||
|
||||
Reference in New Issue
Block a user