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:
parent
ae2653f9dd
commit
4086cc00de
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user