fix(admin): prevent false partner commission cap errors
CI / verify (pull_request) Has been cancelled

Coerce city maxPartnerCommissionRate to Number so Decimal JSON strings do not break sum checks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-22 18:03:32 +08:00
parent addfcfb0b1
commit 85f2c156b0
3 changed files with 17 additions and 8 deletions
@@ -92,9 +92,12 @@ function commissionSumError(
redeemPercent: number, redeemPercent: number,
maxRate: number, maxRate: number,
): string | null { ): string | null {
const sum = orderPercent / 100 + redeemPercent / 100; // API 可能把 Prisma Decimal 序列化为字符串;`"0.05" + 1e-9` 会变成字符串拼接导致误判超限
if (sum > maxRate + 1e-9) { const max = Number(maxRate);
return `订单佣金与核销佣金合计不得超过 ${(maxRate * 100).toFixed(2)}%(当前 ${(sum * 100).toFixed(2)}%`; const sum = Number(orderPercent) / 100 + Number(redeemPercent) / 100;
if (!Number.isFinite(max) || !Number.isFinite(sum)) return '佣金比例无效';
if (sum > max + 1e-9) {
return `订单佣金与核销佣金合计不得超过 ${(max * 100).toFixed(2)}%(当前 ${(sum * 100).toFixed(2)}%`;
} }
return null; return null;
} }
@@ -98,9 +98,12 @@ function flattenDistrictCodes(values: string[] | string[][] | undefined): string
} }
function commissionSumError(orderPercent: number, redeemPercent: number, maxRate: number): string | null { function commissionSumError(orderPercent: number, redeemPercent: number, maxRate: number): string | null {
const sum = orderPercent / 100 + redeemPercent / 100; // API 可能把 Prisma Decimal 序列化为字符串;`"0.05" + 1e-9` 会变成字符串拼接导致误判超限
if (sum > maxRate + 1e-9) { const max = Number(maxRate);
return `订单佣金与核销佣金合计不得超过 ${(maxRate * 100).toFixed(2)}%(当前 ${(sum * 100).toFixed(2)}%`; const sum = Number(orderPercent) / 100 + Number(redeemPercent) / 100;
if (!Number.isFinite(max) || !Number.isFinite(sum)) return '佣金比例无效';
if (sum > max + 1e-9) {
return `订单佣金与核销佣金合计不得超过 ${(max * 100).toFixed(2)}%(当前 ${(sum * 100).toFixed(2)}%`;
} }
return null; return null;
} }
@@ -149,7 +152,7 @@ export default function CityPartnersPage() {
const d = await request<PartnerDetail>(`/admin/partners/${id}`); const d = await request<PartnerDetail>(`/admin/partners/${id}`);
setDetail(d); setDetail(d);
setEditScopeType((d.scopeType as CityPartnerScopeType) || CityPartnerScopeType.CITY_WIDE); setEditScopeType((d.scopeType as CityPartnerScopeType) || CityPartnerScopeType.CITY_WIDE);
setMaxCommissionRate(d.maxPartnerCommissionRate ?? 0.05); setMaxCommissionRate(Number(d.maxPartnerCommissionRate ?? 0.05));
editForm.setFieldsValue({ editForm.setFieldsValue({
name: d.name, name: d.name,
phone: d.phone, phone: d.phone,
@@ -230,7 +233,7 @@ export default function CityPartnersPage() {
async function onCreateCityChange(cityId: string) { async function onCreateCityChange(cityId: string) {
const cityRes = await request<{ maxPartnerCommissionRate?: number }>(`/admin/cities/${cityId}`); const cityRes = await request<{ maxPartnerCommissionRate?: number }>(`/admin/cities/${cityId}`);
setCreateMaxRate(cityRes.maxPartnerCommissionRate ?? 0.05); setCreateMaxRate(Number(cityRes.maxPartnerCommissionRate ?? 0.05));
} }
const columns: ColumnsType<Row> = [ const columns: ColumnsType<Row> = [
@@ -80,6 +80,9 @@ export class AdminCitiesService {
const cityPartners = await this.partnerCityService.listByCity(id); const cityPartners = await this.partnerCityService.listByCity(id);
return serializeBigInt({ return serializeBigInt({
...city, ...city,
// Decimal 经 JSON 会变成字符串,显式 Number 避免前端 `"0.05" + 1e-9` 字符串拼接误判
maxPartnerCommissionRate:
city.maxPartnerCommissionRate != null ? Number(city.maxPartnerCommissionRate) : null,
cityPartners, cityPartners,
storeCount: city._count.stores, storeCount: city._count.stores,
orderCount: city._count.orders, orderCount: city._count.orders,