542 lines
24 KiB
JavaScript
542 lines
24 KiB
JavaScript
import express from 'express'
|
|
import {
|
|
walletAccounts,
|
|
balances,
|
|
boundCards,
|
|
enterpriseOpenApplications,
|
|
personalOpenApplications
|
|
} from '../db/seedWallet.js'
|
|
import { individualCustomers } from '../db/seedCustomer.js'
|
|
import { nextId } from '../utils/id.js'
|
|
import { paginate } from '../utils/pagination.js'
|
|
import { sendOk, sendFail } from '../utils/response.js'
|
|
import { requireAuth } from '../middleware/requireAuth.js'
|
|
import { verifySmsCode } from '../state.js'
|
|
|
|
const router = express.Router()
|
|
router.use(requireAuth)
|
|
|
|
function toWalletAccountVo(a) {
|
|
return {
|
|
id: a.id,
|
|
customerId: a.customerId,
|
|
accountNo: a.accountNo,
|
|
accountType: a.accountType,
|
|
accountName: a.accountName,
|
|
accountRelation: a.accountRelation,
|
|
customerNo: a.customerNo,
|
|
accountStatus: a.accountStatus,
|
|
mainAccountNo: a.mainAccountNo,
|
|
openDate: a.openDate
|
|
}
|
|
}
|
|
|
|
// ================= 账户列表 / 账户主页 =================
|
|
router.post('/wallet-account/page', (req, res) => {
|
|
const { accountName, accountNo, accountType, page, pageSize } = req.body || {}
|
|
let list = walletAccounts
|
|
if (accountName) list = list.filter((a) => a.accountName.includes(accountName))
|
|
if (accountNo) list = list.filter((a) => a.accountNo.includes(accountNo))
|
|
if (accountType) list = list.filter((a) => a.accountType === accountType)
|
|
const result = paginate(list, page, pageSize)
|
|
sendOk(res, { ...result, records: result.records.map(toWalletAccountVo) })
|
|
})
|
|
|
|
router.post('/wallet-account/detail', (req, res) => {
|
|
const { id } = req.body || {}
|
|
const account = walletAccounts.find((a) => a.id === id)
|
|
if (!account) return sendFail(res, '账户不存在', 404)
|
|
const siblings = walletAccounts.filter((a) => a.customerId === account.customerId && a.customerType === account.customerType)
|
|
const findByType = (type) => siblings.find((a) => a.accountType === type)
|
|
const main = findByType('A1')
|
|
const a2 = findByType('A2')
|
|
const a6 = findByType('A6')
|
|
const a7 = findByType('A7')
|
|
sendOk(res, {
|
|
mainAccountNo: main?.accountNo || '',
|
|
mainBalance: main ? balances[main.accountNo] || '0.00' : '0.00',
|
|
a2AccountNo: a2?.accountNo || '',
|
|
a2Balance: a2 ? balances[a2.accountNo] || '0.00' : '0.00',
|
|
a6AccountNo: a6?.accountNo || '',
|
|
a6Balance: a6 ? balances[a6.accountNo] || '0.00' : '0.00',
|
|
a7AccountNo: a7?.accountNo || '',
|
|
a7Balance: a7 ? balances[a7.accountNo] || '0.00' : '0.00'
|
|
})
|
|
})
|
|
|
|
router.post('/wallet-account/close', (req, res) => {
|
|
const { id } = req.body || {}
|
|
const account = walletAccounts.find((a) => a.id === id)
|
|
if (!account) return sendFail(res, '账户不存在', 404)
|
|
account.accountStatus = 'CLOSED'
|
|
sendOk(res, true, '销户成功')
|
|
})
|
|
|
|
router.post('/wallet-account/open-sub-account', (req, res) => {
|
|
const { accountNo } = req.body || {}
|
|
const main = walletAccounts.find((a) => a.accountNo === accountNo)
|
|
if (!main) return sendFail(res, '主账户不存在', 404)
|
|
const id = nextId('walletAccount')
|
|
const seq = nextId('walletAccountSeq')
|
|
const sub = {
|
|
id,
|
|
customerId: main.customerId,
|
|
customerType: main.customerType,
|
|
accountNo: `622${String(seq).padStart(13, '0')}`,
|
|
accountType: 'A7',
|
|
accountName: main.accountName,
|
|
accountRelation: '1',
|
|
customerNo: main.customerNo,
|
|
accountStatus: 'NORMAL',
|
|
mainAccountNo: main.accountNo,
|
|
openDate: new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
}
|
|
walletAccounts.push(sub)
|
|
balances[sub.accountNo] = '0.00'
|
|
sendOk(res, toWalletAccountVo(sub), '分户开立成功')
|
|
})
|
|
|
|
router.post('/wallet-account/update', (req, res) => {
|
|
const { accountNo, mobile, bankCardNumber, bankNo, bankName } = req.body || {}
|
|
const account = walletAccounts.find((a) => a.accountNo === accountNo)
|
|
if (!account) return sendFail(res, '账户不存在', 404)
|
|
const card = boundCards.find((c) => c.accountNo === accountNo)
|
|
if (card) {
|
|
if (mobile !== undefined) card.mobile = mobile
|
|
if (bankCardNumber !== undefined) card.primaryAccount = bankCardNumber
|
|
if (bankNo !== undefined) card.bankNo = bankNo
|
|
if (bankName !== undefined) card.bankName = bankName
|
|
}
|
|
sendOk(res, true, '账户信息变更成功')
|
|
})
|
|
|
|
router.post('/wallet-account/change-mobile', (req, res) => {
|
|
const { accountNo, newMobile } = req.body || {}
|
|
if (!accountNo || !newMobile) return sendFail(res, 'accountNo/newMobile不能为空', 400)
|
|
const card = boundCards.find((c) => c.accountNo === accountNo)
|
|
if (card) card.mobile = newMobile
|
|
sendOk(res, true, '手机号变更成功')
|
|
})
|
|
|
|
router.post('/wallet-account/sync-balance', (req, res) => {
|
|
const { id } = req.body || {}
|
|
const account = walletAccounts.find((a) => a.id === id)
|
|
if (!account) return sendFail(res, '账户不存在', 404)
|
|
sendOk(res, true, '余额同步成功')
|
|
})
|
|
|
|
// 交易明细:mock按查询区间生成一批确定性的示例流水,不做持久化存储
|
|
router.post('/wallet-account/transaction-detail', (req, res) => {
|
|
const { accountNo, channelNo, startDate, endDate, page, rows } = req.body || {}
|
|
if (!accountNo || !channelNo || !startDate || !endDate) {
|
|
return sendFail(res, 'accountNo/channelNo/startDate/endDate不能为空', 400)
|
|
}
|
|
const account = walletAccounts.find((a) => a.accountNo === accountNo)
|
|
const balance = account ? Number(balances[accountNo] || 0) : 0
|
|
const sampleTypes = ['收款', '付款', '提现', '保证金缴纳']
|
|
const detailList = Array.from({ length: 5 }).map((_, idx) => {
|
|
const amount = (1000 + idx * 500).toFixed(2)
|
|
const isIncome = idx % 2 === 0
|
|
return {
|
|
id: idx + 1,
|
|
accountDate: startDate.slice(0, 10),
|
|
tradeTime: startDate,
|
|
detailType: '01',
|
|
transType: sampleTypes[idx % sampleTypes.length],
|
|
transAmount: isIncome ? amount : `-${amount}`,
|
|
balance: (balance - idx * 100).toFixed(2),
|
|
oppAccountNo: `622${String(9000000000000 + idx).slice(0, 13)}`,
|
|
oppAccountName: `示例对方户${idx + 1}`,
|
|
remark: '业务往来款'
|
|
}
|
|
})
|
|
sendOk(res, {
|
|
accountNo,
|
|
accountName: account?.accountName || '',
|
|
curBalance: balance.toFixed(2),
|
|
availBalance: balance.toFixed(2),
|
|
withdrawBalance: balance.toFixed(2),
|
|
preBalance: '0.00',
|
|
detailList,
|
|
page: page ?? 0,
|
|
rows: rows ?? 10000
|
|
})
|
|
})
|
|
|
|
router.post('/wallet-account/withdraw', (req, res) => {
|
|
const { channelNo, withdrawRequestEo } = req.body || {}
|
|
if (!channelNo || !withdrawRequestEo) return sendFail(res, 'channelNo/withdrawRequestEo不能为空', 400)
|
|
const { accountNo, accountName, primaryAccount, amount, verifyCode, transSummary } = withdrawRequestEo
|
|
if (!verifyCode || !verifySmsCode('WALLET_WITHDRAW', boundCards.find((c) => c.accountNo === accountNo)?.mobile || '', verifyCode)) {
|
|
return sendFail(res, '验证码错误或已过期', 400)
|
|
}
|
|
const current = Number(balances[accountNo] || 0)
|
|
const amt = Number(amount)
|
|
if (!amt || amt <= 0) return sendFail(res, '提现金额不合法', 400)
|
|
if (amt > current) return sendFail(res, '账户余额不足', 400)
|
|
balances[accountNo] = (current - amt).toFixed(2)
|
|
sendOk(res, {
|
|
success: true,
|
|
recode: '0000',
|
|
recodeInfo: '提现成功',
|
|
accountNo,
|
|
accountName,
|
|
serialNo: `WD${Date.now()}`,
|
|
transAmount: amount
|
|
}, transSummary || '提现成功')
|
|
})
|
|
|
|
router.post('/wallet-account/margin-pay', (req, res) => {
|
|
const { accountNo, accountName, tradeAmount } = req.body || {}
|
|
const account = walletAccounts.find((a) => a.accountNo === accountNo)
|
|
if (!account) return sendFail(res, '主账户不存在', 404)
|
|
const marginAccount = walletAccounts.find((a) => a.customerId === account.customerId && a.customerType === account.customerType && a.accountType === 'A6')
|
|
if (!marginAccount) return sendFail(res, '该客户未开立保证金账户', 400)
|
|
const amt = Number(tradeAmount)
|
|
const mainBalance = Number(balances[accountNo] || 0)
|
|
if (!amt || amt <= 0) return sendFail(res, '缴纳金额不合法', 400)
|
|
if (amt > mainBalance) return sendFail(res, '主账户余额不足', 400)
|
|
balances[accountNo] = (mainBalance - amt).toFixed(2)
|
|
balances[marginAccount.accountNo] = (Number(balances[marginAccount.accountNo] || 0) + amt).toFixed(2)
|
|
sendOk(res, {
|
|
success: true,
|
|
recode: '0000',
|
|
recodeInfo: '保证金缴纳成功',
|
|
accountNoBzj: marginAccount.accountNo,
|
|
accountNameBzj: accountName || account.accountName
|
|
})
|
|
})
|
|
|
|
router.post('/wallet-account/margin-release', (req, res) => {
|
|
const { accountNo, accountName, tradeAmount } = req.body || {}
|
|
const account = walletAccounts.find((a) => a.accountNo === accountNo)
|
|
if (!account) return sendFail(res, '主账户不存在', 404)
|
|
const marginAccount = walletAccounts.find((a) => a.customerId === account.customerId && a.customerType === account.customerType && a.accountType === 'A6')
|
|
if (!marginAccount) return sendFail(res, '该客户未开立保证金账户', 400)
|
|
const amt = Number(tradeAmount)
|
|
const marginBalance = Number(balances[marginAccount.accountNo] || 0)
|
|
if (!amt || amt <= 0) return sendFail(res, '释放金额不合法', 400)
|
|
if (amt > marginBalance) return sendFail(res, '保证金账户余额不足', 400)
|
|
balances[marginAccount.accountNo] = (marginBalance - amt).toFixed(2)
|
|
balances[accountNo] = (Number(balances[accountNo] || 0) + amt).toFixed(2)
|
|
sendOk(res, {
|
|
success: true,
|
|
recode: '0000',
|
|
recodeInfo: '保证金释放成功',
|
|
accountNoBzj: marginAccount.accountNo,
|
|
accountNameBzj: accountName || account.accountName
|
|
})
|
|
})
|
|
|
|
router.post('/wallet-account/download-statement', (req, res) => {
|
|
const { accountNo, channelNo, startDate, endDate } = req.body || {}
|
|
if (!accountNo || !channelNo || !startDate || !endDate) {
|
|
return sendFail(res, 'accountNo/channelNo/startDate/endDate不能为空', 400)
|
|
}
|
|
sendOk(res, { fileData: Buffer.from(`对账单-${accountNo}-${startDate}~${endDate}`).toString('base64') })
|
|
})
|
|
|
|
router.post('/wallet-account/download-receipt', (req, res) => {
|
|
const { accountNo, channelNo, originalSerialNo } = req.body || {}
|
|
if (!accountNo || !channelNo || !originalSerialNo) {
|
|
return sendFail(res, 'accountNo/channelNo/originalSerialNo不能为空', 400)
|
|
}
|
|
sendOk(res, { fileData: Buffer.from(`回单-${accountNo}-${originalSerialNo}`).toString('base64') })
|
|
})
|
|
|
|
function toBindCardListVo(accountNo) {
|
|
const account = walletAccounts.find((a) => a.accountNo === accountNo)
|
|
const balance = account ? balances[accountNo] || '0.00' : '0.00'
|
|
const cards = boundCards.filter((c) => c.accountNo === accountNo)
|
|
return {
|
|
accountNo,
|
|
accountName: account?.accountName || '',
|
|
idType: 'ID_CARD',
|
|
idNo: '',
|
|
curBalance: balance,
|
|
availBalance: balance,
|
|
withdrawBalance: balance,
|
|
preBalance: '0.00',
|
|
detailList: cards.map((c) => ({ mobile: c.mobile, primaryAccount: c.primaryAccount }))
|
|
}
|
|
}
|
|
|
|
router.post('/wallet-account/bind-card-list', (req, res) => {
|
|
const { accountNo, channelNo } = req.body || {}
|
|
if (!accountNo || !channelNo) return sendFail(res, 'accountNo/channelNo不能为空', 400)
|
|
sendOk(res, toBindCardListVo(accountNo))
|
|
})
|
|
|
|
router.post('/wallet-account/bind-card', (req, res) => {
|
|
const { accountNo, idNo, mobile, bankCardNumber, bankNo, bankName, setDefault, verifyCode } = req.body || {}
|
|
if (!verifyCode || !verifySmsCode('WALLET_BIND_CARD', mobile, verifyCode)) {
|
|
return sendFail(res, '验证码错误或已过期', 400)
|
|
}
|
|
if (setDefault === 'true') {
|
|
boundCards.filter((c) => c.accountNo === accountNo).forEach((c) => (c.setDefault = false))
|
|
}
|
|
boundCards.push({ accountNo, idNo, mobile, primaryAccount: bankCardNumber, bankNo, bankName, setDefault: setDefault === 'true' })
|
|
sendOk(res, { success: true, message: '绑卡成功' })
|
|
})
|
|
|
|
router.post('/wallet-account/unbind-card', (req, res) => {
|
|
const { accountNo, bankCardNumber } = req.body || {}
|
|
const cardsOfAccount = boundCards.filter((c) => c.accountNo === accountNo)
|
|
if (cardsOfAccount.length <= 1) return sendOk(res, { success: false, message: '最后一张绑卡不可解绑' })
|
|
const index = boundCards.findIndex((c) => c.accountNo === accountNo && c.primaryAccount === bankCardNumber)
|
|
if (index === -1) return sendFail(res, '未找到该绑卡记录', 404)
|
|
boundCards.splice(index, 1)
|
|
sendOk(res, { success: true, message: '解绑成功' })
|
|
})
|
|
|
|
// 校验银行卡号是否有效/可用:真实后端方法签名为 @RequestParam(cardNo, channelNo),走查询字符串传参,
|
|
// 不是 JSON Body,故这里读 req.query 而不是 req.body(个人开户申请"银行卡号"字段失焦时触发)。
|
|
// 响应体字段对齐真实后端 QueryCardInfoVo:bankName/bankNo/cardType(0借记卡1准贷记卡2贷记卡)/payBankNo
|
|
router.post('/wallet-account/query-card-info', (req, res) => {
|
|
const { cardNo, channelNo } = req.query || {}
|
|
if (!cardNo || !channelNo) return sendFail(res, 'cardNo/channelNo不能为空', 400)
|
|
if (!/^(\d{16}|\d{19})$/.test(cardNo)) return sendFail(res, '银行卡号格式不正确', 400)
|
|
sendOk(res, { bankName: '中国工商银行', bankNo: '102100099996', cardType: '0', payBankNo: '102100000037' })
|
|
})
|
|
|
|
// ================= 个人开户 =================
|
|
router.post('/wallet-management/personal-opening/query-customer', (req, res) => {
|
|
const { organizationId, customerCode, customerName, certificateNumber, page, pageSize } = req.body || {}
|
|
let list = individualCustomers
|
|
if (organizationId) list = list.filter((c) => c.organizationId === organizationId)
|
|
if (customerCode) list = list.filter((c) => c.customerCode === customerCode)
|
|
if (customerName) list = list.filter((c) => c.customerName.includes(customerName))
|
|
if (certificateNumber) list = list.filter((c) => c.certificateNumber === certificateNumber)
|
|
const result = paginate(list, page, pageSize)
|
|
sendOk(res, {
|
|
...result,
|
|
records: result.records.map((c) => ({
|
|
id: c.id,
|
|
customerCode: c.customerCode,
|
|
customerName: c.customerName,
|
|
certificateType: c.certificateType,
|
|
certificateNumber: c.certificateNumber,
|
|
mobilePhone: c.mobilePhone,
|
|
organizationId: c.organizationId,
|
|
gender: c.gender,
|
|
birthDate: c.birthDate,
|
|
ethnicity: c.ethnicity,
|
|
certificateEffectiveDate: c.certificateEffectiveDate,
|
|
certificateExpiryDate: c.certificateExpiryDate,
|
|
issuingAuthority: c.issuingAuthority,
|
|
certificateAddress: c.certificateAddress,
|
|
occupation: c.occupation
|
|
}))
|
|
})
|
|
})
|
|
|
|
router.post('/wallet-management/personal-opening/create-draft', (req, res) => {
|
|
const body = req.body || {}
|
|
if (!body.customerId || !body.customerName) return sendFail(res, 'customerId/customerName不能为空', 400)
|
|
const applicationNo = `APPPO${String(nextId('personalOpenApplication')).padStart(8, '0')}`
|
|
personalOpenApplications.push({
|
|
applicationNo,
|
|
...body,
|
|
applicationStatus: 'DRAFT',
|
|
failReason: '',
|
|
mainAccountNo: '',
|
|
customerNo: '',
|
|
createdAt: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
|
updatedAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
})
|
|
sendOk(res, { applicationNo }, '草稿创建成功')
|
|
})
|
|
|
|
router.post('/wallet-management/personal-opening/query-progress', (req, res) => {
|
|
const { applicationNo } = req.body || {}
|
|
const application = personalOpenApplications.find((a) => a.applicationNo === applicationNo)
|
|
if (!application) return sendFail(res, '申请单不存在', 404)
|
|
// mock简化:草稿创建后立即模拟"已开立"结果,便于演示完整流程
|
|
if (application.applicationStatus === 'DRAFT') {
|
|
const id = nextId('walletAccount')
|
|
const seq = nextId('walletAccountSeq')
|
|
const newAccountNo = `622${String(seq).padStart(13, '0')}`
|
|
walletAccounts.push({
|
|
id,
|
|
customerId: application.customerId,
|
|
customerType: 'PERSONAL',
|
|
accountNo: newAccountNo,
|
|
accountType: 'A1',
|
|
accountName: application.customerName,
|
|
accountRelation: '0',
|
|
customerNo: `CN${String(application.customerId).padStart(8, '0')}`,
|
|
accountStatus: 'NORMAL',
|
|
mainAccountNo: newAccountNo,
|
|
openDate: new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
})
|
|
balances[newAccountNo] = '0.00'
|
|
application.applicationStatus = 'OPENED'
|
|
application.mainAccountNo = newAccountNo
|
|
application.customerNo = `CN${String(application.customerId).padStart(8, '0')}`
|
|
application.updatedAt = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
}
|
|
sendOk(res, {
|
|
applicationNo: application.applicationNo,
|
|
applicationStatus: application.applicationStatus,
|
|
failReason: application.failReason,
|
|
mainAccountNo: application.mainAccountNo,
|
|
customerNo: application.customerNo,
|
|
createdAt: application.createdAt,
|
|
updatedAt: application.updatedAt
|
|
})
|
|
})
|
|
|
|
// list/detail/update:后端尚未提供,mock 按 enterprise-open-application 四件套的写法模拟实现,
|
|
// 用 applicationNo 定位记录(不假设有数字 id),详见《缺失的后端接口.md》Part 2 新增条目
|
|
function toPersonalOpenListVo(a) {
|
|
return {
|
|
applicationNo: a.applicationNo,
|
|
customerName: a.customerName,
|
|
certificateNumber: a.certificateNumber,
|
|
mobilePhone: a.mobilePhone,
|
|
// 列表页展示开户成功后的主账户账号(未开立成功前为空字符串),不展示银行卡号,
|
|
// 与企业开户申请列表 toEnterpriseOpenListVo 的字段取舍保持一致
|
|
mainAccountNo: a.mainAccountNo || '',
|
|
channelNo: a.channelNo,
|
|
applicationStatus: a.applicationStatus,
|
|
failReason: a.failReason,
|
|
createdAt: a.createdAt,
|
|
updatedAt: a.updatedAt
|
|
}
|
|
}
|
|
|
|
router.post('/wallet-management/personal-opening/list', (req, res) => {
|
|
const { applicationNo, customerNameLike, certificateNumber, applicationStatus, startDate, endDate, page, pageSize } =
|
|
req.body || {}
|
|
let list = personalOpenApplications
|
|
if (applicationNo) list = list.filter((a) => a.applicationNo === applicationNo)
|
|
if (customerNameLike) list = list.filter((a) => a.customerName.includes(customerNameLike))
|
|
if (certificateNumber) list = list.filter((a) => a.certificateNumber === certificateNumber)
|
|
if (applicationStatus) list = list.filter((a) => a.applicationStatus === applicationStatus)
|
|
if (startDate) list = list.filter((a) => a.createdAt >= startDate)
|
|
if (endDate) list = list.filter((a) => a.createdAt <= endDate)
|
|
const result = paginate(list, page, pageSize)
|
|
sendOk(res, { ...result, records: result.records.map(toPersonalOpenListVo) })
|
|
})
|
|
|
|
router.post('/wallet-management/personal-opening/detail', (req, res) => {
|
|
const { applicationNo } = req.body || {}
|
|
const application = personalOpenApplications.find((a) => a.applicationNo === applicationNo)
|
|
if (!application) return sendFail(res, '申请单不存在', 404)
|
|
sendOk(res, application)
|
|
})
|
|
|
|
router.post('/wallet-management/personal-opening/update', (req, res) => {
|
|
const { applicationNo, accountOpenApplicationFileBtoList, ...rest } = req.body || {}
|
|
const application = personalOpenApplications.find((a) => a.applicationNo === applicationNo)
|
|
if (!application) return sendFail(res, '申请单不存在', 404)
|
|
Object.assign(application, rest, {
|
|
// 前端 update 请求的证件照片字段名为 accountOpenApplicationFileBtoList(与 create-draft 的
|
|
// fileList 不同),此处归一存回 fileList,保证 detail 回显与本地演示一致
|
|
...(accountOpenApplicationFileBtoList ? { fileList: accountOpenApplicationFileBtoList } : {}),
|
|
updatedAt: new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
})
|
|
sendOk(res, true)
|
|
})
|
|
|
|
// ================= 企业开户申请 =================
|
|
function toEnterpriseOpenListVo(a) {
|
|
return {
|
|
applicationNo: a.applicationNo,
|
|
enterpriseName: a.enterpriseName,
|
|
businessLicense: a.businessLicense,
|
|
applicationStatus: a.applicationStatus,
|
|
channelNo: a.channelNo,
|
|
confirmTime: a.confirmTime,
|
|
mainAccountNo: a.mainAccountNo,
|
|
customerNo: a.customerNo,
|
|
failReason: a.failReason,
|
|
createdAt: a.createdAt,
|
|
updatedAt: a.updatedAt
|
|
}
|
|
}
|
|
|
|
router.post('/wallet-management/enterprise-open-application/list', (req, res) => {
|
|
const { applicationNo, enterpriseNameLike, applicationStatus, startDate, endDate, page, pageSize } = req.body || {}
|
|
let list = enterpriseOpenApplications
|
|
if (applicationNo) list = list.filter((a) => a.applicationNo === applicationNo)
|
|
if (enterpriseNameLike) list = list.filter((a) => a.enterpriseName.includes(enterpriseNameLike))
|
|
if (applicationStatus) list = list.filter((a) => a.applicationStatus === applicationStatus)
|
|
if (startDate) list = list.filter((a) => a.createdAt >= startDate)
|
|
if (endDate) list = list.filter((a) => a.createdAt <= endDate)
|
|
const result = paginate(list, page, pageSize)
|
|
sendOk(res, { ...result, records: result.records.map(toEnterpriseOpenListVo) })
|
|
})
|
|
|
|
router.post('/wallet-management/enterprise-open-application/create', (req, res) => {
|
|
const body = req.body || {}
|
|
if (!body.enterpriseName) return sendFail(res, '企业名称不能为空', 400)
|
|
const id = nextId('enterpriseOpenApplication')
|
|
const applicationNo = `APPEO${String(id).padStart(8, '0')}`
|
|
const now = new Date().toISOString().slice(0, 19).replace('T', ' ')
|
|
const application = {
|
|
id,
|
|
applicationNo,
|
|
applicationStatus: 'DRAFT',
|
|
failReason: '',
|
|
mainAccountNo: '',
|
|
customerNo: '',
|
|
confirmTime: '',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
fileList: body.enterpriseAccountOpenApplicationFileBtoList || [],
|
|
beneficiaryList: body.enterpriseAccountOpenApplicationBeneficiaryBtoList || [],
|
|
...body
|
|
}
|
|
enterpriseOpenApplications.push(application)
|
|
sendOk(res, { applicationNo }, '申请创建成功')
|
|
})
|
|
|
|
router.post('/wallet-management/enterprise-open-application/detail', (req, res) => {
|
|
const { applicationNo } = req.body || {}
|
|
const application = enterpriseOpenApplications.find((a) => a.applicationNo === applicationNo)
|
|
if (!application) return sendFail(res, '申请单不存在', 404)
|
|
sendOk(res, application)
|
|
})
|
|
|
|
// 已知契约缺口(见《缺失的后端接口.md》Part2-23):list/detail均不返回id,
|
|
// 前端按约定用applicationNo兜底传入id位置,mock按"数字id优先,匹配不到再按applicationNo兜底"两种方式兼容匹配
|
|
router.post('/wallet-management/enterprise-open-application/update', (req, res) => {
|
|
const { id, ...rest } = req.body || {}
|
|
const application = enterpriseOpenApplications.find((a) => a.id === id || a.applicationNo === id)
|
|
if (!application) return sendFail(res, '申请单不存在', 404)
|
|
Object.assign(application, rest, { updatedAt: new Date().toISOString().slice(0, 19).replace('T', ' ') })
|
|
sendOk(res, true)
|
|
})
|
|
|
|
// ================= 企业绑卡 =================
|
|
router.post('/wallet-management/enterprise-bank-card/list', (req, res) => {
|
|
const { accountNo, channelNo } = req.body || {}
|
|
if (!accountNo || !channelNo) return sendFail(res, 'accountNo/channelNo不能为空', 400)
|
|
sendOk(res, toBindCardListVo(accountNo))
|
|
})
|
|
|
|
router.post('/wallet-management/enterprise-bank-card/bind', (req, res) => {
|
|
const { accountNo, idNo, mobile, primaryAccount, bankNo, bankName, setDefault, verifyCode } = req.body || {}
|
|
if (!verifyCode || !verifySmsCode('WALLET_BIND_CARD', mobile, verifyCode)) {
|
|
return sendFail(res, '验证码错误或已过期', 400)
|
|
}
|
|
if (setDefault === 'true') {
|
|
boundCards.filter((c) => c.accountNo === accountNo).forEach((c) => (c.setDefault = false))
|
|
}
|
|
boundCards.push({ accountNo, idNo, mobile, primaryAccount, bankNo, bankName, setDefault: setDefault === 'true' })
|
|
sendOk(res, { success: true, message: '绑卡成功' })
|
|
})
|
|
|
|
router.post('/wallet-management/enterprise-bank-card/unbind', (req, res) => {
|
|
const { accountNo, primaryAccount } = req.body || {}
|
|
const cardsOfAccount = boundCards.filter((c) => c.accountNo === accountNo)
|
|
if (cardsOfAccount.length <= 1) return sendOk(res, { success: false, message: '最后一张绑卡不可解绑' })
|
|
const index = boundCards.findIndex((c) => c.accountNo === accountNo && c.primaryAccount === primaryAccount)
|
|
if (index === -1) return sendOk(res, { success: false, message: '未找到该绑卡记录' })
|
|
boundCards.splice(index, 1)
|
|
sendOk(res, { success: true, message: '解绑成功' })
|
|
})
|
|
|
|
export default router
|