feat: 完善 Transaction 和 Reconciliation API 客户端

Transaction API:
- 新增 deposit() 充值方法
- 新增 withdraw() 提现方法
- 修正 transfer() 端点路径为 /transactions/transfer
- 添加 DepositRequest 和 WithdrawRequest 类型
- 标记需要后端扩展的方法

Reconciliation API:
- 新增 approveAdjustment() 审批补录方法
- 新增 rejectAdjustment() 拒绝补录方法
- 新增 getPendingAdjustments() 获取待审批列表
- 修正 runReconciliation() 端点路径为 /reconciliation/run
- 完善类型定义 (ReconciliationBatch, ManualAdjustment 等)
- 添加状态标签映射

现在前端 API 覆盖率:
- Transaction: 100% (5/5 端点)
- Reconciliation: 100% (8/8 端点)
This commit is contained in:
tangweijie 2026-01-05 18:17:11 +08:00
parent ae2653f9dd
commit 4086cc00de
2 changed files with 427 additions and 122 deletions

View File

@ -1,131 +1,312 @@
// 对账相关API服务
/**
* API
*
*/
import { apiClient } from './client'
import type { PageResponse } from '@/types/api'
/**
*
*/
export interface ReconciliationBatch {
id: number
batchNo: string
reconDate: string
status: string
totalTransactions: number
matchedCount: number
mismatchedCount: number
bankTotal: number
ledgerTotal: number
inTransitNet: number
threeAccountBalanced: boolean
difference: number
createdAt: string
completedAt?: string
batch_no: string
physical_account_id: number
recon_date: string
total_count: number
matched_count: number
mismatch_count: number
status: ReconciliationStatus
bank_total?: string
transit_net?: string
ledger_total?: string
three_account_balanced?: boolean
created_at: string
completed_at?: string
}
/**
*
*/
export type ReconciliationStatus = 'processing' | 'completed' | 'need_review'
/**
*
*/
export interface ReconciliationItem {
id: number
batchId: number
transactionId?: number
bankRefNo?: string
txnType: string
amount: number
bankAmount?: number
ledgerAmount?: number
matchStatus: 'matched' | 'mismatched' | 'bank_only' | 'ledger_only'
matchTime?: string
remarks?: string
batch_id: number
system_txn_no?: string
bank_ref_no?: string
system_amount?: string
bank_amount?: string
diff_amount: string
status: ReconciliationItemStatus
remark?: string
created_at: string
}
/**
*
*/
export type ReconciliationItemStatus =
| 'matched' // 已匹配
| 'system_unreached' // 系统未达
| 'bank_unreached' // 银行未达
| 'amount_mismatch' // 金额不匹配
| 'adjusted' // 已调整
/**
*
*/
export interface ThreeAccountResult {
bankBalance: number
transitNet: number
ledgerTotal: number
isBalanced: boolean
difference: number
physical_account_id: number
bank_balance: string
transit_net: string
ledger_total: string
expected_total: string
difference: string
is_balanced: boolean
verified_at: string
}
/**
*
*/
export interface ManualAdjustment {
id: number
adjustment_no: string
related_txn_no?: string
reconciliation_item_id?: number
adjustment_type: AdjustmentType
account_id: number
amount: string
reason: string
operator: string
approver?: string
status: AdjustmentStatus
created_at: string
approved_at?: string
}
/**
*
*/
export type AdjustmentType = 'add' | 'reverse' | 'modify'
/**
*
*/
export type AdjustmentStatus = 'pending' | 'approved' | 'rejected'
/**
*
*/
export interface CreateReconciliationRequest {
physical_account_id: number
recon_date: string
}
/**
*
*/
export interface CreateAdjustmentRequest {
related_txn_no?: string
reconciliation_item_id?: number
adjustment_type: AdjustmentType
account_id: number
amount: string
reason: string
}
export class ReconciliationAPI {
// 获取对账批次列表
static async getBatches(params?: {
status?: string
startDate?: string
endDate?: string
page?: number
pageSize?: number
}): Promise<{
items: ReconciliationBatch[]
total: number
}> {
return apiClient.get('/reconciliation/batches', params)
// ==================== 核心对账功能 (已对接后端) ====================
/**
*
* 对应后端: POST /api/v1/reconciliation/run
*/
static async runReconciliation(data: CreateReconciliationRequest): Promise<ReconciliationBatch> {
return apiClient.post('/reconciliation/run', data)
}
// 获取对账批次详情
/**
*
* 对应后端: GET /api/v1/reconciliation/batches/:id
*/
static async getBatch(id: number): Promise<ReconciliationBatch> {
return apiClient.get(`/reconciliation/batches/${id}`)
}
// 创建对账批次
static async createBatch(data: {
reconDate: string
accountIds?: number[]
}): Promise<ReconciliationBatch> {
return apiClient.post('/reconciliation/batches', data)
}
// 执行对账
static async executeBatch(id: number): Promise<ReconciliationBatch> {
return apiClient.post(`/reconciliation/batches/${id}/execute`)
}
// 获取对账明细
/**
*
* 对应后端: GET /api/v1/reconciliation/batches/:id/items
*/
static async getBatchItems(batchId: number, params?: {
matchStatus?: string
status?: ReconciliationItemStatus
page?: number
pageSize?: number
}): Promise<{
items: ReconciliationItem[]
total: number
}> {
return apiClient.get(`/reconciliation/batches/${batchId}/items`, params)
page_size?: number
}): Promise<PageResponse<ReconciliationItem>> {
return apiClient.get(`/reconciliation/batches/${batchId}/items`, { params })
}
// 三账校验
/**
*
* 对应后端: GET /api/v1/reconciliation/three-account/:account_id
*/
static async verifyThreeAccounts(accountId: number): Promise<ThreeAccountResult> {
return apiClient.get(`/reconciliation/three-account/${accountId}`)
}
// 手工调整
static async createAdjustment(data: {
batchId: number
transactionId?: number
adjustmentType: string
amount: number
reason: string
operator: string
}): Promise<ReconciliationItem> {
/**
*
* 对应后端: POST /api/v1/reconciliation/adjustments
*/
static async createAdjustment(data: CreateAdjustmentRequest): Promise<ManualAdjustment> {
return apiClient.post('/reconciliation/adjustments', data)
}
/**
*
* 对应后端: POST /api/v1/reconciliation/adjustments/:id/approve
*/
static async approveAdjustment(id: number, approver: string): Promise<ManualAdjustment> {
return apiClient.post(`/reconciliation/adjustments/${id}/approve`, { approver })
}
/**
*
* 对应后端: POST /api/v1/reconciliation/adjustments/:id/reject
*/
static async rejectAdjustment(id: number, approver: string, reason: string): Promise<ManualAdjustment> {
return apiClient.post(`/reconciliation/adjustments/${id}/reject`, { approver, reason })
}
/**
*
* 对应后端: GET /api/v1/reconciliation/adjustments/pending
*/
static async getPendingAdjustments(): Promise<ManualAdjustment[]> {
return apiClient.get('/reconciliation/adjustments/pending')
}
// ==================== 扩展功能 (需要后端支持) ====================
/**
* ()
* @todo
*/
static async getBatches(params?: {
status?: ReconciliationStatus
physical_account_id?: number
start_date?: string
end_date?: string
page?: number
page_size?: number
}): Promise<PageResponse<ReconciliationBatch>> {
return apiClient.get('/reconciliation/batches', { params })
}
// 获取调整记录
static async getAdjustments(batchId: number): Promise<ReconciliationItem[]> {
/**
* ()
* @todo
*/
static async getAdjustments(batchId: number): Promise<ManualAdjustment[]> {
return apiClient.get(`/reconciliation/batches/${batchId}/adjustments`)
}
// 对账统计
/**
* ()
* @todo
*/
static async getStats(params: {
startDate?: string
endDate?: string
accountId?: number
start_date?: string
end_date?: string
physical_account_id?: number
}): Promise<{
totalBatches: number
successRate: number
totalDiscrepancy: number
largestDiscrepancy: number
total_batches: number
success_rate: number
total_discrepancy: string
largest_discrepancy: string
}> {
return apiClient.get('/reconciliation/stats', params)
return apiClient.get('/reconciliation/stats', { params })
}
// 导出对账报告
/**
* ()
* @todo
*/
static async exportReport(batchId: number, format: 'excel' | 'pdf' = 'excel'): Promise<Blob> {
return apiClient.get(`/reconciliation/batches/${batchId}/export`,
{ format },
{ responseType: 'blob' }
)
return apiClient.get(`/reconciliation/batches/${batchId}/export`, {
params: { format },
responseType: 'blob'
})
}
// ==================== 兼容旧方法 ====================
/**
* @deprecated 使 runReconciliation
*/
static async createBatch(data: {
reconDate: string
accountIds?: number[]
}): Promise<ReconciliationBatch> {
return this.runReconciliation({
physical_account_id: data.accountIds?.[0] || 0,
recon_date: data.reconDate,
})
}
/**
* @deprecated 使 runReconciliation
*/
static async executeBatch(id: number): Promise<ReconciliationBatch> {
// 获取批次信息后重新执行对账
const batch = await this.getBatch(id)
return this.runReconciliation({
physical_account_id: batch.physical_account_id,
recon_date: batch.recon_date,
})
}
}
/**
*
*/
export const ReconciliationStatusLabels: Record<ReconciliationStatus, string> = {
processing: '处理中',
completed: '已完成',
need_review: '需要审核',
}
/**
*
*/
export const ReconciliationItemStatusLabels: Record<ReconciliationItemStatus, string> = {
matched: '已匹配',
system_unreached: '系统未达',
bank_unreached: '银行未达',
amount_mismatch: '金额不匹配',
adjusted: '已调整',
}
/**
*
*/
export const AdjustmentTypeLabels: Record<AdjustmentType, string> = {
add: '补录',
reverse: '冲销',
modify: '修改',
}
/**
*
*/
export const AdjustmentStatusLabels: Record<AdjustmentStatus, string> = {
pending: '待审批',
approved: '已审批',
rejected: '已拒绝',
}
export default ReconciliationAPI

View File

@ -1,4 +1,7 @@
// 交易相关API服务
/**
* API
*
*/
import { apiClient } from './client'
import type {
SystemTransaction,
@ -9,54 +12,166 @@ import type {
TransferRequest,
TransferResponse
} from '@/types/transaction'
import type { PageResponse } from '@/types/api'
/**
*
*/
export interface DepositRequest {
account_id: number
amount: string
source_key?: string
remark?: string
}
/**
*
*/
export interface WithdrawRequest {
account_id: number
amount: string
remark?: string
}
export class TransactionAPI {
// 获取交易详情
// ==================== 核心交易操作 ====================
/**
*
* 对应后端: POST /api/v1/transactions/transfer
*/
static async transfer(data: TransferRequest): Promise<SystemTransaction> {
return apiClient.post('/transactions/transfer', data)
}
/**
* ()
* 对应后端: POST /api/v1/transactions/deposit
*/
static async deposit(data: DepositRequest): Promise<SystemTransaction> {
return apiClient.post('/transactions/deposit', data)
}
/**
* ()
* 对应后端: POST /api/v1/transactions/withdraw
*/
static async withdraw(data: WithdrawRequest): Promise<SystemTransaction> {
return apiClient.post('/transactions/withdraw', data)
}
// ==================== 交易查询 ====================
/**
*
* 对应后端: GET /api/v1/transactions/:id
*/
static async getTransaction(id: number): Promise<SystemTransaction> {
return apiClient.get(`/transactions/${id}`)
}
// 根据交易号获取交易
static async getTransactionByNo(txnNo: string): Promise<SystemTransaction> {
return apiClient.get(`/transactions/by-no/${txnNo}`)
/**
*
* 对应后端: GET /api/v1/transactions
*/
static async getTransactions(query: TransactionQuery = {}): Promise<PageResponse<SystemTransaction>> {
return apiClient.get('/transactions', { params: query })
}
// 获取交易列表
// ==================== 兼容旧方法 (保持向后兼容) ====================
/**
* @deprecated 使 getTransactions
*/
static async getTransactionList(query: TransactionQuery = {}): Promise<TransactionListResponse> {
return apiClient.get('/transactions', query)
return apiClient.get('/transactions', { params: query })
}
// 创建交易
/**
* ()
* 注意: 当前后端未提供此端点使 getTransactions + filter
*/
static async getTransactionByNo(txnNo: string): Promise<SystemTransaction> {
const result = await this.getTransactions({ txn_no: txnNo })
if (result.data && result.data.length > 0) {
return result.data[0]
}
throw new Error('交易不存在')
}
// ==================== 扩展功能 (需要后端支持) ====================
/**
* ()
* 注意: 建议使用具体的 transfer/deposit/withdraw
*/
static async createTransaction(data: CreateTransactionRequest): Promise<SystemTransaction> {
return apiClient.post('/transactions', data)
// 根据交易类型调用对应的方法
switch (data.txn_type) {
case 'transfer':
return this.transfer({
from_account_id: data.from_account_id!,
to_account_id: data.to_account_id!,
amount: data.amount,
remark: data.remark,
})
case 'deposit':
return this.deposit({
account_id: data.to_account_id!,
amount: data.amount,
remark: data.remark,
})
case 'withdrawal':
return this.withdraw({
account_id: data.from_account_id!,
amount: data.amount,
remark: data.remark,
})
default:
throw new Error(`不支持的交易类型: ${data.txn_type}`)
}
}
// 提交交易到银行
/**
* ()
* @todo
*/
static async submitToBank(id: number): Promise<{ bankRefNo: string }> {
return apiClient.post(`/transactions/${id}/submit`)
}
// 取消交易
/**
* ()
* @todo
*/
static async cancelTransaction(id: number): Promise<void> {
return apiClient.post(`/transactions/${id}/cancel`)
}
// 重试交易
/**
* ()
* @todo
*/
static async retryTransaction(id: number): Promise<SystemTransaction> {
return apiClient.post(`/transactions/${id}/retry`)
}
// 获取交易状态
/**
* ()
* 注意: 可以通过 getTransaction
*/
static async getTransactionStatus(id: number): Promise<{ status: string, bankRefNo?: string }> {
return apiClient.get(`/transactions/${id}/status`)
const txn = await this.getTransaction(id)
return {
status: txn.status,
bankRefNo: txn.bank_ref_no,
}
}
// 转账操作
static async transfer(data: TransferRequest): Promise<TransferResponse> {
return apiClient.post('/transfer', data)
}
// 查询银行流水
/**
* ()
* @todo
*/
static async getBankStatements(params: {
accountNo: string
startDate: string
@ -67,10 +182,13 @@ export class TransactionAPI {
items: BankTransaction[]
total: number
}> {
return apiClient.get('/bank/statements', params)
return apiClient.get('/bank/statements', { params })
}
// 查询交易统计
/**
* ()
* @todo
*/
static async getTransactionStats(params: {
accountId?: number
startDate?: string
@ -82,22 +200,28 @@ export class TransactionAPI {
failedCount: number
pendingCount: number
}> {
return apiClient.get('/transactions/stats', params)
return apiClient.get('/transactions/stats', { params })
}
// 获取交易历史(分页)
/**
*
* 使 getTransactions + from_account_id
*/
static async getTransactionHistory(params: {
accountId: number
page?: number
pageSize?: number
startDate?: string
endDate?: string
}): Promise<{
items: SystemTransaction[]
total: number
page: number
pageSize: number
}> {
return apiClient.get(`/accounts/${params.accountId}/transactions`, params)
}): Promise<PageResponse<SystemTransaction>> {
return this.getTransactions({
from_account_id: params.accountId,
page: params.page,
page_size: params.pageSize,
start_date: params.startDate,
end_date: params.endDate,
})
}
}
export default TransactionAPI