fix(channels): sanitize card data and format suffixes

This commit is contained in:
2026-09-28 14:26:22 +08:00
parent 99d4a85ece
commit ea4a54f5c6
4 changed files with 51 additions and 25 deletions
+6 -8
View File
@@ -71,14 +71,12 @@ const TXN_SCENES = [
function getChannelIdentifier(channel: ChannelWithAccountNames): string { function getChannelIdentifier(channel: ChannelWithAccountNames): string {
if (channel.channelType === "PAYMENT_CARD") { if (channel.channelType === "PAYMENT_CARD") {
let suffix = channel.cardNumberSuffix?.trim() const digits = (
if ( channel.cardNumberSuffix ||
!suffix && channel.cardNumberFull ||
channel.cardNumberFull && ""
channel.cardNumberFull.length >= 4 ).replace(/\D/g, "")
) { const suffix = digits.length >= 4 ? digits.slice(-4) : digits
suffix = channel.cardNumberFull.slice(-4)
}
return suffix ? `•••• ${suffix}` : "" return suffix ? `•••• ${suffix}` : ""
} }
if (channel.channelType === "E_WALLET") { if (channel.channelType === "E_WALLET") {
+9
View File
@@ -275,11 +275,20 @@ export function ChannelDialog({
cardBrandRequest.current += 1 cardBrandRequest.current += 1
setCardBrand((value as CardBrand | null) || "") setCardBrand((value as CardBrand | null) || "")
}} }}
itemToStringLabel={(brand: CardBrand) =>
brand ? CARD_BRAND_LABELS[brand] || brand : ""
}
itemToStringValue={(brand: CardBrand) =>
brand
? `${brand} ${CARD_BRAND_LABELS[brand] || ""}`
: ""
}
autoHighlight autoHighlight
> >
<ComboboxInput <ComboboxInput
id="cardBrand" id="cardBrand"
placeholder="选择卡组织" placeholder="选择卡组织"
className="w-full"
disabled={isPending} disabled={isPending}
/> />
<ComboboxContent> <ComboboxContent>
+10 -6
View File
@@ -118,6 +118,15 @@ function formatCardNumber(cardNumber: string) {
.trim() .trim()
} }
function getCardSuffix(channel: ChannelWithAccountNames) {
const digits = (
channel.cardNumberSuffix ||
channel.cardNumberFull ||
""
).replace(/\D/g, "")
return digits.length >= 4 ? digits.slice(-4) : digits
}
export function ChannelsView({ export function ChannelsView({
initialChannels, initialChannels,
initialAccounts, initialAccounts,
@@ -402,12 +411,7 @@ export function ChannelsView({
{visibleCardIds.has(channel.id) && {visibleCardIds.has(channel.id) &&
channel.cardNumberFull channel.cardNumberFull
? formatCardNumber(channel.cardNumberFull) ? formatCardNumber(channel.cardNumberFull)
: `•••• ${ : `•••• ${getCardSuffix(channel)}`}
channel.cardNumberSuffix ||
formatCardNumber(
channel.cardNumberFull || ""
).slice(-4)
}`}
</span> </span>
{channel.cardNumberFull && ( {channel.cardNumberFull && (
<> <>
+26 -11
View File
@@ -133,10 +133,16 @@ export async function createChannelAction(
return { success: false, error: "关联的部分账户不存在或已被删除" } return { success: false, error: "关联的部分账户不存在或已被删除" }
} }
// 自动提取或补全卡号后4位 // 自动提取并清洗纯数字卡号及后4位
let suffix = val.cardNumberSuffix?.trim() || null const cleanFull = val.cardNumberFull
if (!suffix && val.cardNumberFull && val.cardNumberFull.length >= 4) { ? val.cardNumberFull.replace(/\D/g, "")
suffix = val.cardNumberFull.slice(-4) : ""
const cleanSuffix = val.cardNumberSuffix
? val.cardNumberSuffix.replace(/\D/g, "")
: ""
let suffix = cleanSuffix.slice(-4) || null
if (!suffix && cleanFull.length >= 4) {
suffix = cleanFull.slice(-4)
} }
const [newChannel] = await db const [newChannel] = await db
@@ -151,8 +157,9 @@ export async function createChannelAction(
region: val.region ? val.region.toUpperCase().trim() : null, region: val.region ? val.region.toUpperCase().trim() : null,
issuerName: val.issuerName?.trim() || null, issuerName: val.issuerName?.trim() || null,
cardType: val.cardType || null, cardType: val.cardType || null,
cardNumberFull: val.cardNumberFull?.trim() || null, cardNumberFull:
cardNumberSuffix: suffix, val.channelType === "PAYMENT_CARD" ? cleanFull || null : null,
cardNumberSuffix: val.channelType === "PAYMENT_CARD" ? suffix : null,
cardBrand: val.cardBrand?.trim() || null, cardBrand: val.cardBrand?.trim() || null,
platform: val.platform?.trim() || null, platform: val.platform?.trim() || null,
platformAccountId: val.platformAccountId?.trim() || null, platformAccountId: val.platformAccountId?.trim() || null,
@@ -205,9 +212,16 @@ export async function updateChannelAction(
return { success: false, error: "关联的部分账户不存在或已被删除" } return { success: false, error: "关联的部分账户不存在或已被删除" }
} }
let suffix = val.cardNumberSuffix?.trim() || null // 自动提取并清洗纯数字卡号及后4位
if (!suffix && val.cardNumberFull && val.cardNumberFull.length >= 4) { const cleanFull = val.cardNumberFull
suffix = val.cardNumberFull.slice(-4) ? val.cardNumberFull.replace(/\D/g, "")
: ""
const cleanSuffix = val.cardNumberSuffix
? val.cardNumberSuffix.replace(/\D/g, "")
: ""
let suffix = cleanSuffix.slice(-4) || null
if (!suffix && cleanFull.length >= 4) {
suffix = cleanFull.slice(-4)
} }
const [updated] = await db const [updated] = await db
@@ -220,8 +234,9 @@ export async function updateChannelAction(
region: val.region ? val.region.toUpperCase().trim() : null, region: val.region ? val.region.toUpperCase().trim() : null,
issuerName: val.issuerName?.trim() || null, issuerName: val.issuerName?.trim() || null,
cardType: val.cardType || null, cardType: val.cardType || null,
cardNumberFull: val.cardNumberFull?.trim() || null, cardNumberFull:
cardNumberSuffix: suffix, val.channelType === "PAYMENT_CARD" ? cleanFull || null : null,
cardNumberSuffix: val.channelType === "PAYMENT_CARD" ? suffix : null,
cardBrand: val.cardBrand?.trim() || null, cardBrand: val.cardBrand?.trim() || null,
platform: val.platform?.trim() || null, platform: val.platform?.trim() || null,
platformAccountId: val.platformAccountId?.trim() || null, platformAccountId: val.platformAccountId?.trim() || null,