feat(edit): 定价试算开发、SKU 样式功能修改

This commit is contained in:
R524809
2026-09-29 17:50:19 +08:00
parent 84b230e69a
commit 0a303414d8
52 changed files with 4278 additions and 1393 deletions
@@ -0,0 +1,81 @@
import { z } from 'zod';
export const commissionImportRequestSchema = z
.object({
platform_id: z.uuid(),
version: z.string().trim().min(1).max(100),
source_reference: z.string().trim().min(3).max(500),
captured_on: z.iso.date(),
document: z.unknown().refine((v) => v !== undefined, '请上传 JSON'),
})
.strict();
export const commissionImportPublishSchema =
commissionImportRequestSchema.extend({
preview_hash: z.string().regex(/^[a-f0-9]{64}$/),
confirm_scope: z.literal(true),
});
export type CommissionImportRequest = z.infer<
typeof commissionImportRequestSchema
>;
export type CommissionImportPublish = z.infer<
typeof commissionImportPublishSchema
>;
export type CommissionBand = {
min_price: string | null;
max_price: string | null;
min_inclusive: boolean;
max_inclusive: boolean;
commission_rate: string;
};
export type CommissionImportCategory = {
source_category_id: string;
parent_source_id: string;
name: string;
parent_name: string;
bands: CommissionBand[];
status: 'eligible' | 'excluded';
issues: string[];
change: 'added' | 'changed' | 'unchanged';
mapped_types: number;
};
export type CommissionImportSummary = {
categories: number;
rules: number;
eligible: number;
excluded: number;
mapped_types: number;
removed: number;
};
export type CommissionImportPreview = {
preview_hash: string;
previous_import_id: string | null;
categories: CommissionImportCategory[];
summary: CommissionImportSummary;
};
export type CommissionImportVersion = {
id: string;
platform_id: string;
version: string;
source_reference: string;
captured_on: string;
activated_at: string;
is_current: boolean;
summary: CommissionImportSummary;
};
export type CommissionImportDetail = CommissionImportVersion & {
categories: CommissionImportCategory[];
};
export type CommissionRulesResult = {
category_name: string;
currency_code: string;
version: string;
source: string;
effective_from: string;
bands: Array<CommissionBand & { id: string }>;
};
export type CommissionDetails = CommissionBand & {
category_name: string;
currency_code: string;
version: string;
source: string;
};
+1
View File
@@ -9,3 +9,4 @@ export * from './online-products';
export * from './pricing';
export * from './pricing-admin';
export * from './category-commissions';
+2
View File
@@ -1,3 +1,4 @@
import type { CommissionDetails } from './category-commissions';
import { z } from 'zod';
const decimal = z.string().regex(/^\d{1,9}(\.\d{1,8})?$/);
@@ -132,6 +133,7 @@ export type PricingPreviewRequest = z.infer<typeof pricingPreviewRequestSchema>;
export type PricingSelection = z.infer<typeof pricingSelectionSchema>;
export type PricingPolicyRequest = z.infer<typeof pricingPolicyRequestSchema>;
export type PricingOption = {
commission_details?: CommissionDetails;
id: string;
transport_method_id: string;
transport_name: string;
+4
View File
@@ -202,6 +202,8 @@ export type ProductEditorSnapshot = {
};
default_physical_spec: PhysicalSpec;
default_package_spec: PackageSpec;
package_weight_mode: 'common' | 'variant';
package_dimensions_mode: 'common' | 'variant';
};
profile: {
id: string;
@@ -507,6 +509,8 @@ export const saveProductEditorDraftRequestSchema = z
supply_note: nullableText(4_000),
default_physical_spec: physicalSpec,
default_package_spec: packageSpec,
package_weight_mode: z.enum(['common', 'variant']).optional(),
package_dimensions_mode: z.enum(['common', 'variant']).optional(),
})
.strict(),
profile: z
@@ -0,0 +1,59 @@
SET search_path TO atlas, public;
-- 平台共享参考规则;所有写入口均要求系统管理员,批次保存原始来源以供追溯。
CREATE TABLE commission_imports (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
platform_id UUID NOT NULL REFERENCES platforms(id) ON DELETE RESTRICT,
version TEXT NOT NULL CHECK(length(version) BETWEEN 1 AND 100),
source_reference TEXT NOT NULL,
captured_on DATE NOT NULL,
document JSONB NOT NULL,
content_hash CHAR(64) NOT NULL,
categories JSONB NOT NULL,
summary JSONB NOT NULL,
activated_by UUID NOT NULL REFERENCES users(id) ON DELETE RESTRICT,
activated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
is_current BOOLEAN NOT NULL DEFAULT true,
UNIQUE(platform_id,version),
UNIQUE(platform_id,content_hash),
UNIQUE(platform_id,id)
);
CREATE UNIQUE INDEX commission_import_current ON commission_imports(platform_id) WHERE is_current;
ALTER TABLE commission_category_rules ADD COLUMN import_id UUID REFERENCES commission_imports(id) ON DELETE RESTRICT;
ALTER TABLE platform_product_category_commission_mappings ADD COLUMN import_id UUID;
ALTER TABLE platform_product_category_commission_mappings ADD CONSTRAINT commission_mapping_import_platform_fk
FOREIGN KEY(platform_id,import_id) REFERENCES commission_imports(platform_id,id) ON DELETE RESTRICT;
CREATE UNIQUE INDEX commission_import_category_key ON commission_categories(platform_id,source_category_key)
WHERE source_category_key LIKE 'maozierp:%';
CREATE INDEX commission_rules_import ON commission_category_rules(import_id);
CREATE INDEX commission_mapping_import ON platform_product_category_commission_mappings(import_id);
-- 已发布版本及数值不可改写;换版仅允许结束有效期,仍保留历史报价引用。
CREATE FUNCTION guard_commission_import_history() RETURNS trigger LANGUAGE plpgsql SET search_path=atlas,public AS $$
BEGIN
IF TG_TABLE_NAME='commission_imports' THEN
IF TG_OP='DELETE' OR (to_jsonb(NEW)-'is_current') IS DISTINCT FROM (to_jsonb(OLD)-'is_current') OR NOT OLD.is_current OR NEW.is_current THEN
RAISE EXCEPTION '佣金版本不可改写' USING ERRCODE='23514';
END IF;
ELSIF OLD.import_id IS NOT NULL THEN
IF TG_OP='DELETE' OR (to_jsonb(NEW)-'effective_to'-'status') IS DISTINCT FROM (to_jsonb(OLD)-'effective_to'-'status') OR OLD.effective_to IS NOT NULL OR NEW.effective_to IS NULL OR NEW.status<>'archived' THEN
RAISE EXCEPTION '已导入的佣金规则不可改写,请导入新版本' USING ERRCODE='23514';
END IF;
END IF;
IF TG_OP='DELETE' THEN RETURN OLD; END IF;
RETURN NEW;
END $$;
CREATE TRIGGER commission_import_history BEFORE UPDATE OR DELETE ON commission_imports FOR EACH ROW EXECUTE FUNCTION guard_commission_import_history();
CREATE TRIGGER commission_rule_history BEFORE UPDATE OR DELETE ON commission_category_rules FOR EACH ROW EXECUTE FUNCTION guard_commission_import_history();
CREATE TRIGGER commission_mapping_history BEFORE UPDATE OR DELETE ON platform_product_category_commission_mappings FOR EACH ROW EXECUTE FUNCTION guard_commission_import_history();
-- 规则与导入版本必须同平台;应用代码不能通过关联写入跨平台规则。
CREATE FUNCTION check_commission_rule_import_platform() RETURNS trigger LANGUAGE plpgsql SET search_path=atlas,public AS $$
BEGIN
IF NEW.import_id IS NOT NULL AND NOT EXISTS (
SELECT 1 FROM commission_imports i JOIN commission_categories c ON c.platform_id=i.platform_id
WHERE i.id=NEW.import_id AND c.id=NEW.commission_category_id
) THEN RAISE EXCEPTION '佣金规则与导入平台不一致' USING ERRCODE='23514'; END IF;
RETURN NEW;
END $$;
CREATE TRIGGER commission_rule_import_platform BEFORE INSERT OR UPDATE ON commission_category_rules FOR EACH ROW EXECUTE FUNCTION check_commission_rule_import_platform();
@@ -0,0 +1,30 @@
SET search_path TO atlas, public;
-- 包装重量与尺寸可分别由商品通用值或逐 SKU 值填写;空草稿也需要保存模式。
ALTER TABLE products
ADD COLUMN package_weight_mode TEXT NOT NULL DEFAULT 'common'
CHECK (package_weight_mode IN ('common', 'variant')),
ADD COLUMN package_dimensions_mode TEXT NOT NULL DEFAULT 'common'
CHECK (package_dimensions_mode IN ('common', 'variant'));
-- 旧编辑器在每个 SKU 都有对应覆盖值时即展示逐 SKU 表单,迁移时保留该显示模式。
UPDATE products p
SET package_weight_mode = 'variant'
WHERE EXISTS (
SELECT 1 FROM product_variants v WHERE v.product_id = p.id AND v.deleted_at IS NULL
)
AND NOT EXISTS (
SELECT 1 FROM product_variants v
WHERE v.product_id = p.id AND v.deleted_at IS NULL AND v.shipping_weight_g IS NULL
);
UPDATE products p
SET package_dimensions_mode = 'variant'
WHERE EXISTS (
SELECT 1 FROM product_variants v WHERE v.product_id = p.id AND v.deleted_at IS NULL
)
AND NOT EXISTS (
SELECT 1 FROM product_variants v
WHERE v.product_id = p.id AND v.deleted_at IS NULL
AND (v.package_length_mm IS NULL OR v.package_width_mm IS NULL OR v.package_height_mm IS NULL)
);