fix: preserve legacy payment account display
This commit is contained in:
+21
-18
@@ -104,6 +104,7 @@ type PaymentAccountDetail struct {
|
|||||||
AlipayQRURL string `json:"alipay_qr_url"`
|
AlipayQRURL string `json:"alipay_qr_url"`
|
||||||
VerifyStatus string `json:"verify_status"`
|
VerifyStatus string `json:"verify_status"`
|
||||||
IsDefault bool `json:"is_default"`
|
IsDefault bool `json:"is_default"`
|
||||||
|
Masked bool `json:"masked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type QuoteInput struct {
|
type QuoteInput struct {
|
||||||
@@ -297,7 +298,7 @@ func (s *Service) PaymentAccounts(ctx context.Context, tenantID, memberID int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) PaymentAccountDetails(ctx context.Context, tenantID, memberID int64) ([]PaymentAccountDetail, error) {
|
func (s *Service) PaymentAccountDetails(ctx context.Context, tenantID, memberID int64) ([]PaymentAccountDetail, error) {
|
||||||
if !s.ready || s.repository == nil {
|
if s.repository == nil {
|
||||||
return nil, ErrUnavailable
|
return nil, ErrUnavailable
|
||||||
}
|
}
|
||||||
rows, err := s.repository.PaymentAccountDetails(ctx, tenantID, memberID)
|
rows, err := s.repository.PaymentAccountDetails(ctx, tenantID, memberID)
|
||||||
@@ -306,33 +307,35 @@ func (s *Service) PaymentAccountDetails(ctx context.Context, tenantID, memberID
|
|||||||
}
|
}
|
||||||
items := make([]PaymentAccountDetail, 0, len(rows))
|
items := make([]PaymentAccountDetail, 0, len(rows))
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
holderName, err := s.decrypt(row.HolderCipher)
|
holderName, accountNo := row.HolderMask, row.AccountNoMask
|
||||||
if err != nil {
|
identityNo, digitalRMBCode := row.IdentityNoMask, row.DigitalRMBCodeMask
|
||||||
return nil, store.ErrPaymentAccountInvalid
|
masked := true
|
||||||
}
|
if s.ready {
|
||||||
accountNo, err := s.decrypt(row.AccountNoCipher)
|
decryptedHolder, holderErr := s.decrypt(row.HolderCipher)
|
||||||
if err != nil {
|
decryptedAccount, accountErr := s.decrypt(row.AccountNoCipher)
|
||||||
return nil, store.ErrPaymentAccountInvalid
|
if holderErr == nil && accountErr == nil {
|
||||||
}
|
holderName, accountNo, masked = decryptedHolder, decryptedAccount, false
|
||||||
identityNo := ""
|
|
||||||
if row.IdentityNoCipher != "" {
|
if row.IdentityNoCipher != "" {
|
||||||
identityNo, err = s.decrypt(row.IdentityNoCipher)
|
if value, decryptErr := s.decrypt(row.IdentityNoCipher); decryptErr == nil {
|
||||||
if err != nil {
|
identityNo = value
|
||||||
return nil, store.ErrPaymentAccountInvalid
|
} else {
|
||||||
|
masked = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
digitalRMBCode := ""
|
|
||||||
if row.DigitalRMBCodeCipher != "" {
|
if row.DigitalRMBCodeCipher != "" {
|
||||||
digitalRMBCode, err = s.decrypt(row.DigitalRMBCodeCipher)
|
if value, decryptErr := s.decrypt(row.DigitalRMBCodeCipher); decryptErr == nil {
|
||||||
if err != nil {
|
digitalRMBCode = value
|
||||||
return nil, store.ErrPaymentAccountInvalid
|
} else {
|
||||||
|
masked = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
items = append(items, PaymentAccountDetail{
|
items = append(items, PaymentAccountDetail{
|
||||||
ID: row.ID, BankName: row.BankName, HolderName: holderName,
|
ID: row.ID, BankName: row.BankName, HolderName: holderName,
|
||||||
IdentityNo: identityNo, AccountNo: accountNo, DigitalRMBCode: digitalRMBCode,
|
IdentityNo: identityNo, AccountNo: accountNo, DigitalRMBCode: digitalRMBCode,
|
||||||
WechatQRURL: row.WechatQRURL, AlipayQRURL: row.AlipayQRURL,
|
WechatQRURL: row.WechatQRURL, AlipayQRURL: row.AlipayQRURL,
|
||||||
VerifyStatus: verifyStatusName(row.VerifyStatus), IsDefault: row.IsDefault == 1,
|
VerifyStatus: verifyStatusName(row.VerifyStatus), IsDefault: row.IsDefault == 1, Masked: masked,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
|
|||||||
@@ -168,6 +168,28 @@ func TestPaymentAccountDetailsReturnsOriginalFields(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPaymentAccountDetailsFallsBackToMasksWhenHistoricalKeyIsUnavailable(t *testing.T) {
|
||||||
|
repository := &fakeRepository{details: []store.PaymentAccount{{
|
||||||
|
ID: 8, BankName: "中国银行", HolderCipher: "legacy-holder", HolderMask: "张*",
|
||||||
|
IdentityNoCipher: "legacy-id", IdentityNoMask: "1101**********123X",
|
||||||
|
AccountNoCipher: "legacy-account", AccountNoMask: "6222********1234",
|
||||||
|
DigitalRMBCodeCipher: "legacy-rmb", DigitalRMBCodeMask: "12****78",
|
||||||
|
WechatQRURL: "/wechat.png", AlipayQRURL: "/alipay.png", VerifyStatus: 2,
|
||||||
|
}}}
|
||||||
|
service := New(repository, &redis.Client{}, config.AuthConfig{
|
||||||
|
FinanceMasterKey: "different-finance-master-key-with-32-characters",
|
||||||
|
})
|
||||||
|
items, err := service.PaymentAccountDetails(context.Background(), 1, 2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if len(items) != 1 || !items[0].Masked || items[0].HolderName != "张*" ||
|
||||||
|
items[0].IdentityNo != "1101**********123X" || items[0].AccountNo != "6222********1234" ||
|
||||||
|
items[0].DigitalRMBCode != "12****78" {
|
||||||
|
t.Fatalf("unexpected masked fallback: %#v", items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestCreatePaymentAccountDoesNotStoreEmptyDigitalRMBCode(t *testing.T) {
|
func TestCreatePaymentAccountDoesNotStoreEmptyDigitalRMBCode(t *testing.T) {
|
||||||
repository := &paymentAccountRepository{}
|
repository := &paymentAccountRepository{}
|
||||||
service := New(repository, &redis.Client{}, config.AuthConfig{
|
service := New(repository, &redis.Client{}, config.AuthConfig{
|
||||||
|
|||||||
@@ -156,14 +156,18 @@ func (s *Store) PaymentAccounts(ctx context.Context, tenantID, memberID int64) (
|
|||||||
return items, err
|
return items, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// PaymentAccountDetails returns encrypted fields only for the authenticated account owner.
|
// PaymentAccountDetails returns encrypted fields and their masked fallbacks for the authenticated owner.
|
||||||
func (s *Store) PaymentAccountDetails(ctx context.Context, tenantID, memberID int64) ([]PaymentAccount, error) {
|
func (s *Store) PaymentAccountDetails(ctx context.Context, tenantID, memberID int64) ([]PaymentAccount, error) {
|
||||||
items := make([]PaymentAccount, 0)
|
items := make([]PaymentAccount, 0)
|
||||||
err := s.db.SelectContext(ctx, &items, `SELECT a.id,a.account_type,a.bank_code,a.bank_name,
|
err := s.db.SelectContext(ctx, &items, `SELECT a.id,a.account_type,a.bank_code,a.bank_name,
|
||||||
COALESCE(a.holder_cipher,'') AS holder_cipher,
|
COALESCE(a.holder_cipher,'') AS holder_cipher,
|
||||||
|
COALESCE(a.holder_mask,'') AS holder_mask,
|
||||||
COALESCE(a.identity_no_cipher,'') AS identity_no_cipher,
|
COALESCE(a.identity_no_cipher,'') AS identity_no_cipher,
|
||||||
|
COALESCE(a.identity_no_mask,'') AS identity_no_mask,
|
||||||
COALESCE(a.account_no_cipher,'') AS account_no_cipher,
|
COALESCE(a.account_no_cipher,'') AS account_no_cipher,
|
||||||
|
COALESCE(a.account_no_mask,'') AS account_no_mask,
|
||||||
COALESCE(a.digital_rmb_code_cipher,'') AS digital_rmb_code_cipher,
|
COALESCE(a.digital_rmb_code_cipher,'') AS digital_rmb_code_cipher,
|
||||||
|
COALESCE(a.digital_rmb_code_mask,'') AS digital_rmb_code_mask,
|
||||||
COALESCE(a.wechat_qr_attachment_id,0) AS wechat_qr_attachment_id,
|
COALESCE(a.wechat_qr_attachment_id,0) AS wechat_qr_attachment_id,
|
||||||
COALESCE(a.alipay_qr_attachment_id,0) AS alipay_qr_attachment_id,
|
COALESCE(a.alipay_qr_attachment_id,0) AS alipay_qr_attachment_id,
|
||||||
COALESCE(wechat.file_url,'') AS wechat_qr_url,
|
COALESCE(wechat.file_url,'') AS wechat_qr_url,
|
||||||
|
|||||||
@@ -54,6 +54,7 @@
|
|||||||
<section v-if="paymentAccounts.length" class="m-bound-accounts">
|
<section v-if="paymentAccounts.length" class="m-bound-accounts">
|
||||||
<h2>已绑定的取出信息</h2>
|
<h2>已绑定的取出信息</h2>
|
||||||
<div v-for="account in paymentAccounts" :key="account.id" class="m-bound-account m-card">
|
<div v-for="account in paymentAccounts" :key="account.id" class="m-bound-account m-card">
|
||||||
|
<p v-if="account.masked" class="m-bound-account-hint">历史资料仅展示脱敏信息,重新绑定后可查看完整资料</p>
|
||||||
<div><span>姓名</span><strong>{{ account.holder_name }}</strong></div>
|
<div><span>姓名</span><strong>{{ account.holder_name }}</strong></div>
|
||||||
<div><span>身份证号</span><strong>{{ account.identity_no || '未填写' }}</strong></div>
|
<div><span>身份证号</span><strong>{{ account.identity_no || '未填写' }}</strong></div>
|
||||||
<div><span>银行名</span><strong>{{ account.bank_name }}</strong></div>
|
<div><span>银行名</span><strong>{{ account.bank_name }}</strong></div>
|
||||||
@@ -282,6 +283,7 @@
|
|||||||
digital_rmb_code: string;
|
digital_rmb_code: string;
|
||||||
wechat_qr_url: string;
|
wechat_qr_url: string;
|
||||||
alipay_qr_url: string;
|
alipay_qr_url: string;
|
||||||
|
masked: boolean;
|
||||||
}
|
}
|
||||||
const paymentAccounts = ref<PaymentAccountDetail[]>([]);
|
const paymentAccounts = ref<PaymentAccountDetail[]>([]);
|
||||||
const content = ref<any>();
|
const content = ref<any>();
|
||||||
|
|||||||
Reference in New Issue
Block a user