feat(M01-B): 严格转换旧库订单金额
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
export const MYSQL_UNSIGNED_INT_MAX = 4_294_967_295;
|
||||
|
||||
export interface LegacyMoneyOptions {
|
||||
nullable?: boolean;
|
||||
maxCents?: number;
|
||||
field?: string;
|
||||
}
|
||||
|
||||
function describeField(field: string | undefined): string {
|
||||
return field ? ` for ${field}` : '';
|
||||
}
|
||||
|
||||
export function legacyDecimalToCents(
|
||||
value: unknown,
|
||||
options: LegacyMoneyOptions = {}
|
||||
): number | null {
|
||||
const field = describeField(options.field);
|
||||
|
||||
if (value === null || value === undefined) {
|
||||
if (options.nullable) {
|
||||
return null;
|
||||
}
|
||||
throw new Error(`Legacy money value${field} cannot be null.`);
|
||||
}
|
||||
|
||||
if (typeof value !== 'string' && typeof value !== 'number') {
|
||||
throw new Error(`Legacy money value${field} must be a decimal string or number.`);
|
||||
}
|
||||
|
||||
if (typeof value === 'number' && !Number.isFinite(value)) {
|
||||
throw new Error(`Legacy money value${field} must be finite.`);
|
||||
}
|
||||
|
||||
const decimal = String(value);
|
||||
const match = /^(\d+)(?:\.(\d{1,2}))?$/.exec(decimal);
|
||||
if (!match) {
|
||||
throw new Error(
|
||||
`Legacy money value${field} must be a non-negative decimal with at most two fractional digits.`
|
||||
);
|
||||
}
|
||||
|
||||
const wholeCents = BigInt(match[1]) * 100n;
|
||||
const fraction = (match[2] ?? '').padEnd(2, '0');
|
||||
const cents = wholeCents + BigInt(fraction || '0');
|
||||
const maxCents = options.maxCents ?? MYSQL_UNSIGNED_INT_MAX;
|
||||
|
||||
if (!Number.isSafeInteger(maxCents) || maxCents < 0) {
|
||||
throw new Error('Legacy money maxCents must be a non-negative safe integer.');
|
||||
}
|
||||
|
||||
if (cents > BigInt(maxCents)) {
|
||||
throw new Error(`Legacy money value${field} exceeds the target cents column limit.`);
|
||||
}
|
||||
|
||||
return Number(cents);
|
||||
}
|
||||
Reference in New Issue
Block a user