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