Files
qipai/backend/src/db/legacy-money.ts
T
2026-06-18 09:40:40 +08:00

57 lines
1.6 KiB
TypeScript

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