瀏覽代碼

feat(web-admin): add receive account snapshot value helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu 3 月之前
父節點
當前提交
d946b91dd1
共有 2 個文件被更改,包括 187 次插入 和 0 次删除
  1. 68 0
      web-admin/src/utils/oa-receive-account-value.test.ts
  2. 119 0
      web-admin/src/utils/oa-receive-account-value.ts

+ 68 - 0
web-admin/src/utils/oa-receive-account-value.test.ts

@@ -0,0 +1,68 @@
+import { describe, it } from 'node:test'
+import assert from 'node:assert/strict'
+import {
+  ACCOUNT_TYPE_LABEL,
+  formatReceiveAccountDisplay,
+  formatReceiveAccountLine,
+  isReceiveAccountSnapshot,
+  normalizeReceiveAccountModelValue,
+  type ReceiveAccountSnapshot
+} from './oa-receive-account-value'
+
+const alipay: ReceiveAccountSnapshot = {
+  id: '1',
+  accountType: 'alipay',
+  accountName: '张三',
+  accountNo: '13800138000',
+  bankName: '',
+  bankProvince: '',
+  bankCity: '',
+  bankBranch: '',
+  remark: '日常'
+}
+
+const personal: ReceiveAccountSnapshot = {
+  id: '2',
+  accountType: 'personal',
+  accountName: '李四',
+  accountNo: '6222',
+  bankName: '工行',
+  bankProvince: '安徽省',
+  bankCity: '合肥市',
+  bankBranch: '长江中路支行',
+  remark: ''
+}
+
+describe('oa-receive-account-value', () => {
+  it('type labels', () => {
+    assert.equal(ACCOUNT_TYPE_LABEL.alipay, '支付宝')
+    assert.equal(ACCOUNT_TYPE_LABEL.personal, '个人银行卡')
+    assert.equal(ACCOUNT_TYPE_LABEL.corporate, '对公银行账户')
+  })
+  it('isReceiveAccountSnapshot', () => {
+    assert.equal(isReceiveAccountSnapshot(alipay), true)
+    assert.equal(isReceiveAccountSnapshot({ id: '1' }), false)
+  })
+  it('normalize always array', () => {
+    assert.deepEqual(normalizeReceiveAccountModelValue(null), [])
+    assert.deepEqual(normalizeReceiveAccountModelValue(alipay), [alipay])
+    assert.deepEqual(normalizeReceiveAccountModelValue([alipay, personal]), [alipay, personal])
+  })
+  it('format line alipay omits bank', () => {
+    const line = formatReceiveAccountLine(alipay)
+    assert.ok(line.includes('支付宝'))
+    assert.ok(line.includes('张三'))
+    assert.ok(!line.includes('工行'))
+    assert.ok(line.includes('日常'))
+  })
+  it('format line personal full', () => {
+    const line = formatReceiveAccountLine(personal)
+    assert.ok(line.includes('个人银行卡'))
+    assert.ok(line.includes('安徽省 / 合肥市'))
+    assert.ok(line.includes('长江中路支行'))
+  })
+  it('format display multiline', () => {
+    const text = formatReceiveAccountDisplay([alipay, personal])
+    assert.ok(text.includes('\n'))
+  })
+})

+ 119 - 0
web-admin/src/utils/oa-receive-account-value.ts

@@ -0,0 +1,119 @@
+export type ReceiveAccountType = 'alipay' | 'personal' | 'corporate'
+
+export type ReceiveAccountSnapshot = {
+  id: string
+  accountType: ReceiveAccountType
+  accountName: string
+  accountNo: string
+  bankName: string
+  bankProvince: string
+  bankCity: string
+  bankBranch: string
+  remark: string
+}
+
+export const ACCOUNT_TYPE_LABEL: Record<ReceiveAccountType, string> = {
+  alipay: '支付宝',
+  personal: '个人银行卡',
+  corporate: '对公银行账户'
+}
+
+const SNAPSHOT_FIELDS: (keyof ReceiveAccountSnapshot)[] = [
+  'id',
+  'accountType',
+  'accountName',
+  'accountNo',
+  'bankName',
+  'bankProvince',
+  'bankCity',
+  'bankBranch',
+  'remark'
+]
+
+function isReceiveAccountType(v: unknown): v is ReceiveAccountType {
+  return v === 'alipay' || v === 'personal' || v === 'corporate'
+}
+
+export function isReceiveAccountSnapshot(v: unknown): v is ReceiveAccountSnapshot {
+  if (!v || typeof v !== 'object') return false
+  const o = v as Record<string, unknown>
+  if (!isReceiveAccountType(o.accountType)) return false
+  for (const key of SNAPSHOT_FIELDS) {
+    if (typeof o[key] !== 'string') return false
+  }
+  return String(o.id || '').trim() !== ''
+}
+
+function trimSnapshot(v: ReceiveAccountSnapshot): ReceiveAccountSnapshot {
+  return {
+    id: String(v.id).trim(),
+    accountType: v.accountType,
+    accountName: String(v.accountName).trim(),
+    accountNo: String(v.accountNo).trim(),
+    bankName: String(v.bankName).trim(),
+    bankProvince: String(v.bankProvince).trim(),
+    bankCity: String(v.bankCity).trim(),
+    bankBranch: String(v.bankBranch).trim(),
+    remark: String(v.remark).trim()
+  }
+}
+
+export function normalizeReceiveAccountModelValue(
+  raw: unknown
+): ReceiveAccountSnapshot[] {
+  if (Array.isArray(raw)) {
+    return raw.filter(isReceiveAccountSnapshot).map(trimSnapshot)
+  }
+  if (isReceiveAccountSnapshot(raw)) {
+    return [trimSnapshot(raw)]
+  }
+  return []
+}
+
+export function formatReceiveAccountLine(item: ReceiveAccountSnapshot): string {
+  const parts: string[] = [
+    ACCOUNT_TYPE_LABEL[item.accountType],
+    item.accountName,
+    item.accountNo
+  ]
+  if (item.accountType === 'personal' || item.accountType === 'corporate') {
+    if (item.bankName) parts.push(item.bankName)
+    if (item.bankProvince || item.bankCity) {
+      parts.push(`${item.bankProvince} / ${item.bankCity}`)
+    }
+    if (item.bankBranch) parts.push(item.bankBranch)
+  }
+  if (item.remark) parts.push(item.remark)
+  return parts.filter(Boolean).join(' · ')
+}
+
+export function formatReceiveAccountDisplay(raw: unknown): string {
+  return normalizeReceiveAccountModelValue(raw)
+    .map(formatReceiveAccountLine)
+    .join('\n')
+}
+
+export function optionLabelFromRow(row: ReceiveAccountSnapshot): string {
+  return [
+    ACCOUNT_TYPE_LABEL[row.accountType],
+    row.accountName,
+    row.accountNo
+  ]
+    .filter(Boolean)
+    .join(' · ')
+}
+
+export function snapshotFromRow(row: any): ReceiveAccountSnapshot {
+  const accountType = row?.accountType
+  return {
+    id: String(row?.id ?? '').trim(),
+    accountType: isReceiveAccountType(accountType) ? accountType : 'personal',
+    accountName: String(row?.accountName ?? '').trim(),
+    accountNo: String(row?.accountNo ?? '').trim(),
+    bankName: String(row?.bankName ?? '').trim(),
+    bankProvince: String(row?.bankProvince ?? '').trim(),
+    bankCity: String(row?.bankCity ?? '').trim(),
+    bankBranch: String(row?.bankBranch ?? '').trim(),
+    remark: String(row?.remark ?? '').trim()
+  }
+}