feat(edit): 商品编辑页优化

This commit is contained in:
R524809
2026-09-21 17:44:50 +08:00
parent 75fa2b60f3
commit 05747d6f22
20 changed files with 1006 additions and 244 deletions
@@ -208,6 +208,8 @@ export class ProductEditorService {
purchase_cost_cny: string | null;
listing_id: string;
offer_id: string | null;
description: string | null;
tags: unknown;
status: string;
selling_price: string | null;
list_price: string | null;
@@ -227,7 +229,8 @@ export class ProductEditorService {
'missing' | 'ambiguous' | 'confirmed' | null;
}>(
`SELECT v.id, v.source_variant_key, v.source_sku, v.display_name, v.sort_order,
v.purchase_cost_cny::text AS purchase_cost_cny, l.id AS listing_id, l.offer_id, l.status, l.selling_price::text,
v.purchase_cost_cny::text AS purchase_cost_cny, l.id AS listing_id, l.offer_id,
l.description, l.tags, l.status, l.selling_price::text,
l.list_price::text, l.currency_code, v.specification_values,
v.physical_length_mm::text, v.physical_width_mm::text, v.physical_height_mm::text,
v.net_weight_g::text, v.physical_spec_status,
@@ -353,6 +356,13 @@ export class ProductEditorService {
display_name: variant.display_name,
sort_order: variant.sort_order,
purchase_cost_cny: variant.purchase_cost_cny,
tags: Array.isArray(variant.tags)
? variant.tags.filter(
(value): value is string =>
typeof value === 'string',
)
: [],
description: variant.description,
specification_values: filterSpecificationValues(
variant.specification_values,
),
@@ -865,11 +875,14 @@ export class ProductEditorService {
);
await transaction.query(
`UPDATE atlas.product_variant_platform_listings
SET offer_id = $1, store_id = $2, revision = revision + 1, updated_at = now()
WHERE profile_id = $3 AND product_variant_id = $4 AND status <> 'imported'`,
SET offer_id = $1, store_id = $2, tags = $3::jsonb, description = $4,
revision = revision + 1, updated_at = now()
WHERE profile_id = $5 AND product_variant_id = $6 AND status <> 'imported'`,
[
variant.offer_id,
input.profile.target_store_id,
JSON.stringify(variant.tags),
variant.description,
profile.id,
variant.id,
],
@@ -953,8 +966,10 @@ export class ProductEditorService {
});
await transaction.query(
`INSERT INTO atlas.product_variant_platform_listings
(workspace_id, product_id, profile_id, platform_id, product_variant_id, store_id, offer_id, currency_code)
SELECT current_setting('app.workspace_id')::uuid, $1, $2, $3, $4, $5, $6, s.currency_code
(workspace_id, product_id, profile_id, platform_id, product_variant_id, store_id, offer_id,
currency_code, tags, description)
SELECT current_setting('app.workspace_id')::uuid, $1, $2, $3, $4, $5, $6, s.currency_code,
$7::jsonb, $8
FROM atlas.stores s WHERE s.id = $5`,
[
productId,
@@ -963,6 +978,8 @@ export class ProductEditorService {
id,
input.profile.target_store_id,
variant.offer_id,
JSON.stringify(variant.tags),
variant.description,
],
);
}
@@ -1481,9 +1498,13 @@ export class ProductEditorService {
values: string[],
unchanged: boolean,
) {
// 商品颜色允许多选;该用户输入规则优先于部分 Ozon 类目 schema 的单值标记。
const isProductColor = definition.name.trim() === '商品颜色';
const allowsMultipleValues = definition.is_collection || isProductColor;
const maxValueCount = isProductColor ? 100 : definition.max_value_count;
if (
values.length > definition.max_value_count ||
(!definition.is_collection && values.length !== 1)
values.length > maxValueCount ||
(!allowsMultipleValues && values.length !== 1)
)
throw new ApiError(
422,
@@ -52,6 +52,11 @@ type ScopedValidationIssue = {
issue: ProductEditorValidation['profile']['issues'][number];
};
/** 商品颜色是 SKU 维度的业务必填属性,即使部分 Ozon 类目将其标为选填。 */
function isRequiredVariantAttribute(definition: ProductAttributeSchema) {
return definition.is_required || definition.name.trim() === '商品颜色';
}
/**
* 计算商品草稿的首次上架就绪状态。
* 草稿保存保持宽松,这里的结果供发布前校验和页面问题汇总使用。
@@ -298,7 +303,10 @@ export class ProductEditorValidationOperations {
});
for (const definition of schema.filter(
(item) => item.is_required && item.is_aspect && !item.editor_field,
(item) =>
isRequiredVariantAttribute(item) &&
item.is_aspect &&
!item.editor_field,
)) {
if (
!hasConfirmedValue(