refactor(后台表单): 抽取共享整数解析器
- pageUtils 新增严格的 parsePositiveInteger / parseNonNegativeInteger 与落库用 parsePositiveIntegerOrZero / parseNonNegativeIntegerOrZero。 - 账号配置、会员档位、充值商品三页删除各自的同名本地解析器,统一走共享实现,消除同名不同语义的校验漂移。 - 解析从 parseInt 改为 Number + isSafeInteger,'12abc' 一类脏输入不再被截断成 12。
This commit is contained in:
@@ -23,7 +23,7 @@ import type {
|
||||
} from '../api/adminApiTypes';
|
||||
import { useAdminWriteConfirm } from '../components/useAdminWriteConfirm';
|
||||
import { membershipPlanLabels } from '../config/membershipDisplay';
|
||||
import { handlePageError } from './pageUtils';
|
||||
import { handlePageError, parsePositiveIntegerOrZero } from './pageUtils';
|
||||
|
||||
interface AdminMembershipPlanPageProps {
|
||||
token: string;
|
||||
@@ -106,11 +106,12 @@ export function AdminMembershipPlanPage({
|
||||
}
|
||||
|
||||
setErrorMessage('');
|
||||
const normalizedMonthPriceCents = parsePositiveInteger(monthPriceCents);
|
||||
const normalizedYearPriceCents = parsePositiveInteger(yearPriceCents);
|
||||
const normalizedMonthPriceCents =
|
||||
parsePositiveIntegerOrZero(monthPriceCents);
|
||||
const normalizedYearPriceCents = parsePositiveIntegerOrZero(yearPriceCents);
|
||||
const normalizedConcurrentJobLimit =
|
||||
parsePositiveInteger(concurrentJobLimit);
|
||||
const normalizedPeriodPoints = parsePositiveInteger(periodPoints);
|
||||
parsePositiveIntegerOrZero(concurrentJobLimit);
|
||||
const normalizedPeriodPoints = parsePositiveIntegerOrZero(periodPoints);
|
||||
if (plan !== 'normal') {
|
||||
if (!normalizedMonthPriceCents || !normalizedYearPriceCents) {
|
||||
setErrorMessage('月价与年价必须是大于 0 的整数分');
|
||||
@@ -454,13 +455,3 @@ function formatPlanLimits(entry: ProfileMembershipPlanAdminResponse) {
|
||||
function formatPrice(priceCents: number) {
|
||||
return `¥${(priceCents / 100).toFixed(2)}`;
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value: string) {
|
||||
const parsed = parseInteger(value);
|
||||
return parsed > 0 ? parsed : 0;
|
||||
}
|
||||
|
||||
function parseInteger(value: string) {
|
||||
const parsed = Number(value);
|
||||
return Number.isSafeInteger(parsed) ? parsed : 0;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,11 @@ import {
|
||||
} from '../api/adminApiClient';
|
||||
import type { ProfileWalletConfigAdminResponse } from '../api/adminApiTypes';
|
||||
import { useAdminWriteConfirm } from '../components/useAdminWriteConfirm';
|
||||
import { handlePageError } from './pageUtils';
|
||||
import {
|
||||
handlePageError,
|
||||
parseNonNegativeInteger,
|
||||
parsePositiveInteger,
|
||||
} from './pageUtils';
|
||||
|
||||
interface AdminProfileWalletConfigPageProps {
|
||||
token: string;
|
||||
@@ -200,16 +204,3 @@ export function AdminProfileWalletConfigPage({
|
||||
</AdminPage>
|
||||
);
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value: string) {
|
||||
const parsed = Number(value);
|
||||
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : 0;
|
||||
}
|
||||
|
||||
function parseNonNegativeInteger(value: string): number | null {
|
||||
if (value.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ import {
|
||||
} from '../api/adminApiClient';
|
||||
import type { ProfileRechargeProductConfigAdminResponse } from '../api/adminApiTypes';
|
||||
import { useAdminWriteConfirm } from '../components/useAdminWriteConfirm';
|
||||
import { handlePageError } from './pageUtils';
|
||||
import {
|
||||
handlePageError,
|
||||
parseNonNegativeIntegerOrZero,
|
||||
parsePositiveIntegerOrZero,
|
||||
} from './pageUtils';
|
||||
|
||||
interface AdminRechargeProductPageProps {
|
||||
token: string;
|
||||
@@ -104,10 +108,10 @@ export function AdminRechargeProductPage({
|
||||
const response = await upsertProfileRechargeProduct(token, {
|
||||
productId: productId.trim(),
|
||||
title: title.trim(),
|
||||
priceCents: parsePositiveInteger(priceCents),
|
||||
priceCents: parsePositiveIntegerOrZero(priceCents),
|
||||
kind: 'points',
|
||||
pointsAmount: parsePositiveInteger(pointsAmount),
|
||||
bonusPoints: parseNonNegativeInteger(bonusPoints),
|
||||
pointsAmount: parsePositiveIntegerOrZero(pointsAmount),
|
||||
bonusPoints: parseNonNegativeIntegerOrZero(bonusPoints),
|
||||
durationDays: 0,
|
||||
badgeLabel: badgeLabel.trim(),
|
||||
description: description.trim(),
|
||||
@@ -358,16 +362,6 @@ function formatPrice(priceCents: number) {
|
||||
return `¥${(priceCents / 100).toFixed(2)}`;
|
||||
}
|
||||
|
||||
function parsePositiveInteger(value: string) {
|
||||
const parsed = parseInteger(value);
|
||||
return parsed > 0 ? parsed : 0;
|
||||
}
|
||||
|
||||
function parseNonNegativeInteger(value: string) {
|
||||
const parsed = parseInteger(value);
|
||||
return parsed > 0 ? parsed : 0;
|
||||
}
|
||||
|
||||
function parseInteger(value: string) {
|
||||
const parsed = Number.parseInt(value, 10);
|
||||
if (!Number.isFinite(parsed)) {
|
||||
|
||||
@@ -31,3 +31,31 @@ export function formatUnknownJson(value: unknown) {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
/** 严格解析正安全整数:空串、小数、非正数与非数字都返回 `null`,用于表单校验。 */
|
||||
export function parsePositiveInteger(value: string): number | null {
|
||||
if (value.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
return Number.isSafeInteger(parsed) && parsed > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
/** 严格解析非负安全整数:空串、小数、负数与非数字都返回 `null`,用于表单校验。 */
|
||||
export function parseNonNegativeInteger(value: string): number | null {
|
||||
if (value.trim() === '') {
|
||||
return null;
|
||||
}
|
||||
const parsed = Number(value);
|
||||
return Number.isSafeInteger(parsed) && parsed >= 0 ? parsed : null;
|
||||
}
|
||||
|
||||
/** 落库用宽松解析:非法输入按 `0` 处理,避免后台保存被输入抖动打断。 */
|
||||
export function parsePositiveIntegerOrZero(value: string): number {
|
||||
return parsePositiveInteger(value) ?? 0;
|
||||
}
|
||||
|
||||
/** 落库用宽松解析:非法输入按 `0` 处理,避免后台保存被输入抖动打断。 */
|
||||
export function parseNonNegativeIntegerOrZero(value: string): number {
|
||||
return parseNonNegativeInteger(value) ?? 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user