|
|
import { roundHalfUp } from "@/lib/math";
|
|
|
|
|
|
export type MarkupType = "percent" | "fixed";
|
|
|
|
|
|
export const MARKUP_PERCENT_MIN = 0;
|
|
|
export const MARKUP_PERCENT_MAX = 30;
|
|
|
export const MARKUP_PERCENT_DECIMALS = 2;
|
|
|
export const MARKUP_FIXED_MAX = 99_999.99;
|
|
|
|
|
|
export type ParsedMarkupInput = {
|
|
|
markupType: MarkupType;
|
|
|
markupPercent: number;
|
|
|
markupFixedAmount: number | null;
|
|
|
};
|
|
|
|
|
|
export type MarkupValidationError =
|
|
|
| { code: "INVALID_BODY"; message: string }
|
|
|
| { code: "PERCENT_TOO_HIGH"; message: string }
|
|
|
| { code: "INVALID_PERCENT"; message: string }
|
|
|
| { code: "INVALID_FIXED"; message: string };
|
|
|
|
|
|
function parseMarkupType(value: unknown): MarkupType | null {
|
|
|
if (value === undefined || value === null || value === "") {
|
|
|
return "percent";
|
|
|
}
|
|
|
if (value === "percent" || value === "fixed") {
|
|
|
return value;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
function parsePercentValue(value: unknown): number | null {
|
|
|
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
|
return null;
|
|
|
}
|
|
|
return roundHalfUp(value, MARKUP_PERCENT_DECIMALS);
|
|
|
}
|
|
|
|
|
|
function parseFixedAmount(value: unknown): number | null {
|
|
|
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
|
return null;
|
|
|
}
|
|
|
if (value < 0 || value > MARKUP_FIXED_MAX) {
|
|
|
return null;
|
|
|
}
|
|
|
return roundHalfUp(value, 2);
|
|
|
}
|
|
|
|
|
|
/** 解析并校验加价配置入参(宿主 API / 管理端共用) */
|
|
|
export function parseMarkupInput(body: {
|
|
|
markup_type?: unknown;
|
|
|
markup_percent?: unknown;
|
|
|
markup_fixed_amount?: unknown;
|
|
|
}): ParsedMarkupInput | MarkupValidationError {
|
|
|
const markupType = parseMarkupType(body.markup_type);
|
|
|
if (!markupType) {
|
|
|
return { code: "INVALID_BODY", message: "加价方式无效,请使用 percent 或 fixed" };
|
|
|
}
|
|
|
|
|
|
if (markupType === "fixed") {
|
|
|
const fixed = parseFixedAmount(body.markup_fixed_amount);
|
|
|
if (fixed === null) {
|
|
|
if (
|
|
|
typeof body.markup_fixed_amount === "number" &&
|
|
|
body.markup_fixed_amount > MARKUP_FIXED_MAX
|
|
|
) {
|
|
|
return {
|
|
|
code: "INVALID_FIXED",
|
|
|
message: `固定加价金额不能超过 $${MARKUP_FIXED_MAX.toFixed(2)}`,
|
|
|
};
|
|
|
}
|
|
|
return {
|
|
|
code: "INVALID_FIXED",
|
|
|
message: "请提供有效的固定加价金额(≥ 0)",
|
|
|
};
|
|
|
}
|
|
|
return {
|
|
|
markupType: "fixed",
|
|
|
markupPercent: 0,
|
|
|
markupFixedAmount: fixed,
|
|
|
};
|
|
|
}
|
|
|
|
|
|
const percent = parsePercentValue(body.markup_percent);
|
|
|
if (percent === null) {
|
|
|
return { code: "INVALID_PERCENT", message: "请提供有效的加价比例(0~30%,步长 0.01%)" };
|
|
|
}
|
|
|
if (percent > MARKUP_PERCENT_MAX) {
|
|
|
return { code: "PERCENT_TOO_HIGH", message: "加价比例不能超过 30%" };
|
|
|
}
|
|
|
if (percent < MARKUP_PERCENT_MIN) {
|
|
|
return { code: "INVALID_PERCENT", message: "请提供有效的加价比例(0~30%,步长 0.01%)" };
|
|
|
}
|
|
|
|
|
|
return {
|
|
|
markupType: "percent",
|
|
|
markupPercent: percent,
|
|
|
markupFixedAmount: null,
|
|
|
};
|
|
|
}
|