feat(edit): 定价试算开发、SKU 样式功能修改
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import { z } from 'zod';
|
||||
import type { CommissionImportCategory } from '@atlas/contracts';
|
||||
import { ApiError } from '../common/api-error.js';
|
||||
import { D } from './pricing-engine.js';
|
||||
|
||||
const sourceId = z
|
||||
.union([
|
||||
z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
||||
z.string().regex(/^[1-9]\d{0,17}$/),
|
||||
])
|
||||
.transform(String);
|
||||
const leaf = z
|
||||
.object({
|
||||
id: z.union([z.string(), z.number()]),
|
||||
label: z.string().max(200),
|
||||
value: z.string().max(50),
|
||||
})
|
||||
.strict();
|
||||
const category = z
|
||||
.object({
|
||||
id: z.union([z.string(), z.number()]),
|
||||
label: z.string().trim().min(1).max(200),
|
||||
value: z.string().max(200),
|
||||
cate_id: sourceId,
|
||||
children: z.array(leaf).length(3),
|
||||
})
|
||||
.strict();
|
||||
const root = category
|
||||
.omit({ children: true })
|
||||
.extend({ children: z.array(category).min(1).max(2000) });
|
||||
const documentSchema = z
|
||||
.object({
|
||||
code: z.literal(1),
|
||||
msg: z.string(),
|
||||
data: z.array(root).min(1).max(100),
|
||||
})
|
||||
.strict();
|
||||
const fail = (message: string): never => {
|
||||
throw new ApiError(422, 'COMMISSION_IMPORT_INVALID', message);
|
||||
};
|
||||
|
||||
/** 仅在导入时解析来源文字;报价读取结构化区间,不解析展示标签。 */
|
||||
export function parseCommissionDocument(
|
||||
document: unknown,
|
||||
): CommissionImportCategory[] {
|
||||
if (Buffer.byteLength(JSON.stringify(document) ?? '') > 2 * 1024 * 1024)
|
||||
fail('JSON 不能超过 2 MiB');
|
||||
const parsed = documentSchema.safeParse(document);
|
||||
if (!parsed.success)
|
||||
return fail(
|
||||
'JSON 格式不正确:应包含一级类目、二级类目和每类完整的三个价格档位',
|
||||
);
|
||||
const ids = new Set<string>();
|
||||
const result: CommissionImportCategory[] = [];
|
||||
for (const group of parsed.data.data) {
|
||||
if (ids.has(group.cate_id)) fail(`类目 ID 重复:${group.cate_id}`);
|
||||
ids.add(group.cate_id);
|
||||
for (const item of group.children) {
|
||||
if (ids.has(item.cate_id)) fail(`类目 ID 重复:${item.cate_id}`);
|
||||
ids.add(item.cate_id);
|
||||
const bands = item.children.map((tier, index) => {
|
||||
const value = /^([123]),(\d{1,2}(?:\.\d{1,2})?)$/.exec(
|
||||
tier.value,
|
||||
);
|
||||
const prefix = [
|
||||
'售价 ≤ 1500₽',
|
||||
'1500₽ < 售价 ≤ 5000₽',
|
||||
'售价 > 5000₽',
|
||||
][index];
|
||||
const label = /^(.*?)\s*\((\d{1,2}(?:\.\d{1,2})?)%\)$/.exec(
|
||||
tier.label,
|
||||
);
|
||||
if (
|
||||
!value ||
|
||||
!label ||
|
||||
value[1] !== String(index + 1) ||
|
||||
label[1].trim() !== prefix ||
|
||||
!D(value[2]).eq(label[2])
|
||||
)
|
||||
return fail(
|
||||
`${item.label}:档位或标签百分比与 value 不一致`,
|
||||
);
|
||||
return {
|
||||
min_price: ['0', '1500', '5000'][index],
|
||||
max_price: ['1500', '5000', null][index],
|
||||
min_inclusive: false,
|
||||
max_inclusive: index < 2,
|
||||
commission_rate: D(value[2]).div(100).toFixed(6),
|
||||
};
|
||||
});
|
||||
result.push({
|
||||
source_category_id: item.cate_id,
|
||||
parent_source_id: group.cate_id,
|
||||
name: item.label,
|
||||
parent_name: group.label,
|
||||
bands,
|
||||
status: 'eligible',
|
||||
issues: [],
|
||||
change: 'added',
|
||||
mapped_types: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (result.length > 2000) fail('二级类目不能超过 2,000 个');
|
||||
return result;
|
||||
}
|
||||
Reference in New Issue
Block a user