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(); 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; }