""" 银行流水API路由 """ from typing import Any, List, Optional from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy import select, func, and_, or_ from sqlalchemy.ext.asyncio import AsyncSession from loguru import logger from app.database import get_async_session from app.models.bank_transaction import BankTransaction from app.schemas.bank import ( BankTransactionCreate, BankTransactionUpdate, BankTransactionResponse, BankTransactionListResponse, ) router = APIRouter() @router.get("", response_model=BankTransactionListResponse) async def list_bank_transactions( page: int = Query(1, ge=1, description="页码"), size: int = Query(10, ge=1, le=100, description="每页数量"), transaction_id: str = Query(None, description="流水ID"), account_no: str = Query(None, description="账号"), transaction_type: str = Query(None, description="交易类型"), is_suspicious: bool = Query(None, description="是否可疑交易"), db: AsyncSession = Depends(get_async_session), ): """ 获取银行流水列表(分页查询) """ logger.info(f"获取银行流水列表: page={page}, size={size}") # 构建查询 query = select(BankTransaction) # 添加过滤条件 conditions = [] if transaction_id: conditions.append(BankTransaction.transaction_id.ilike(f"%{transaction_id}%")) if account_no: conditions.append(BankTransaction.account_no == account_no) if transaction_type: conditions.append(BankTransaction.transaction_type == transaction_type) if is_suspicious is not None: conditions.append(BankTransaction.is_suspicious == is_suspicious) if conditions: query = query.where(and_(*conditions)) # 获取总数 count_query = select(func.count()).select_from(BankTransaction) if conditions: count_query = count_query.where(and_(*conditions)) total_result = await db.execute(count_query) total = total_result.scalar() # 分页 query = query.offset((page - 1) * size).limit(size) # 执行查询 result = await db.execute(query) records = result.scalars().all() # 转换为响应格式 response_records = [] for transaction in records: response_records.append({ "id": transaction.id, "transaction_id": transaction.transaction_id, "account_no": transaction.account_no, "account_name": transaction.account_name, "bank_name": transaction.bank_name, "transaction_date": transaction.transaction_date.isoformat() if transaction.transaction_date else None, "transaction_time": transaction.transaction_time.isoformat() if transaction.transaction_time else None, "transaction_type": transaction.transaction_type, "transaction_amount": transaction.transaction_amount, "balance": transaction.balance, "counterparty_account_no": transaction.counterparty_account_no, "counterparty_account_name": transaction.counterparty_account_name, "counterparty_bank_name": transaction.counterparty_bank_name, "voucher_no": transaction.voucher_no, "transaction_purpose": transaction.transaction_purpose, "is_cross_border": transaction.is_cross_border, "currency": transaction.currency, "amount_cny": transaction.amount_cny, "exchange_rate": transaction.exchange_rate, "is_large_amount": transaction.is_large_amount, "is_suspicious": transaction.is_suspicious, "suspicious_reason": transaction.suspicious_reason, "is_reconciled": transaction.is_reconciled, "reconciled_time": transaction.reconciled_time.isoformat() if transaction.reconciled_time else None, }) return BankTransactionListResponse( records=response_records, total=total, page=page, size=size, ) @router.get("/{transaction_id}", response_model=BankTransactionResponse) async def get_bank_transaction(transaction_id: str, db: AsyncSession = Depends(get_async_session)): """ 根据ID获取银行流水详细信息 """ logger.info(f"获取银行流水详情: {transaction_id}") query = select(BankTransaction).where(BankTransaction.transaction_id == transaction_id) result = await db.execute(query) transaction = result.scalar_one_or_none() if not transaction: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"银行流水不存在: {transaction_id}", ) # 转换为响应格式 response = { "id": transaction.id, "transaction_id": transaction.transaction_id, "account_no": transaction.account_no, "account_name": transaction.account_name, "bank_name": transaction.bank_name, "transaction_date": transaction.transaction_date.isoformat() if transaction.transaction_date else None, "transaction_time": transaction.transaction_time.isoformat() if transaction.transaction_time else None, "transaction_type": transaction.transaction_type, "transaction_amount": transaction.transaction_amount, "balance": transaction.balance, "counterparty_account_no": transaction.counterparty_account_no, "counterparty_account_name": transaction.counterparty_account_name, "counterparty_bank_name": transaction.counterparty_bank_name, "voucher_no": transaction.voucher_no, "transaction_purpose": transaction.transaction_purpose, "is_cross_border": transaction.is_cross_border, "currency": transaction.currency, "amount_cny": transaction.amount_cny, "exchange_rate": transaction.exchange_rate, "is_large_amount": transaction.is_large_amount, "is_suspicious": transaction.is_suspicious, "suspicious_reason": transaction.suspicious_reason, "is_reconciled": transaction.is_reconciled, "reconciled_time": transaction.reconciled_time.isoformat() if transaction.reconciled_time else None, } return response @router.post("", response_model=BankTransactionResponse, status_code=status.HTTP_201_CREATED) async def create_bank_transaction(transaction: BankTransactionCreate, db: AsyncSession = Depends(get_async_session)): """ 创建新的银行流水 """ logger.info(f"创建银行流水: {transaction.account_no}") # 生成流水ID query = select(func.count()).select_from(BankTransaction) result = await db.execute(query) count = result.scalar() transaction_id = f"BTX{2024}{(count + 1):06d}" # 创建新流水 new_transaction = BankTransaction( transaction_id=transaction_id, account_no=transaction.account_no, account_name=transaction.account_name, bank_name=transaction.bank_name, transaction_date=transaction.transaction_date, transaction_time=transaction.transaction_time, transaction_type=transaction.transaction_type, transaction_amount=transaction.transaction_amount, balance=transaction.balance, counterparty_account_no=transaction.counterparty_account_no, counterparty_account_name=transaction.counterparty_account_name, counterparty_bank_name=transaction.counterparty_bank_name, voucher_no=transaction.voucher_no, transaction_purpose=transaction.transaction_purpose, is_cross_border=transaction.is_cross_border, currency=transaction.currency or "CNY", amount_cny=transaction.amount_cny, exchange_rate=transaction.exchange_rate or 1.0, is_large_amount=transaction.is_large_amount, is_suspicious=transaction.is_suspicious, suspicious_reason=transaction.suspicious_reason, is_reconciled=transaction.is_reconciled, reconciled_time=transaction.reconciled_time, ) db.add(new_transaction) await db.commit() await db.refresh(new_transaction) # 转换为响应格式 response = { "id": new_transaction.id, "transaction_id": new_transaction.transaction_id, "account_no": new_transaction.account_no, "account_name": new_transaction.account_name, "bank_name": new_transaction.bank_name, "transaction_date": new_transaction.transaction_date.isoformat() if new_transaction.transaction_date else None, "transaction_time": new_transaction.transaction_time.isoformat() if new_transaction.transaction_time else None, "transaction_type": new_transaction.transaction_type, "transaction_amount": new_transaction.transaction_amount, "balance": new_transaction.balance, "counterparty_account_no": new_transaction.counterparty_account_no, "counterparty_account_name": new_transaction.counterparty_account_name, "counterparty_bank_name": new_transaction.counterparty_bank_name, "voucher_no": new_transaction.voucher_no, "transaction_purpose": new_transaction.transaction_purpose, "is_cross_border": new_transaction.is_cross_border, "currency": new_transaction.currency, "amount_cny": new_transaction.amount_cny, "exchange_rate": new_transaction.exchange_rate, "is_large_amount": new_transaction.is_large_amount, "is_suspicious": new_transaction.is_suspicious, "suspicious_reason": new_transaction.suspicious_reason, "is_reconciled": new_transaction.is_reconciled, "reconciled_time": new_transaction.reconciled_time.isoformat() if new_transaction.reconciled_time else None, } return response @router.put("/{transaction_id}", response_model=BankTransactionResponse) async def update_bank_transaction( transaction_id: str, transaction_update: BankTransactionUpdate, db: AsyncSession = Depends(get_async_session), ): """ 更新银行流水 """ logger.info(f"更新银行流水: {transaction_id}") query = select(BankTransaction).where(BankTransaction.transaction_id == transaction_id) result = await db.execute(query) transaction = result.scalar_one_or_none() if not transaction: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"银行流水不存在: {transaction_id}", ) # 更新字段 update_data = transaction_update.model_dump(exclude_unset=True) for key, value in update_data.items(): if hasattr(transaction, key): setattr(transaction, key, value) await db.commit() await db.refresh(transaction) # 转换为响应格式 response = { "id": transaction.id, "transaction_id": transaction.transaction_id, "account_no": transaction.account_no, "account_name": transaction.account_name, "bank_name": transaction.bank_name, "transaction_date": transaction.transaction_date.isoformat() if transaction.transaction_date else None, "transaction_time": transaction.transaction_time.isoformat() if transaction.transaction_time else None, "transaction_type": transaction.transaction_type, "transaction_amount": transaction.transaction_amount, "balance": transaction.balance, "counterparty_account_no": transaction.counterparty_account_no, "counterparty_account_name": transaction.counterparty_account_name, "counterparty_bank_name": transaction.counterparty_bank_name, "voucher_no": transaction.voucher_no, "transaction_purpose": transaction.transaction_purpose, "is_cross_border": transaction.is_cross_border, "currency": transaction.currency, "amount_cny": transaction.amount_cny, "exchange_rate": transaction.exchange_rate, "is_large_amount": transaction.is_large_amount, "is_suspicious": transaction.is_suspicious, "suspicious_reason": transaction.suspicious_reason, "is_reconciled": transaction.is_reconciled, "reconciled_time": transaction.reconciled_time.isoformat() if transaction.reconciled_time else None, } return response @router.delete("/{transaction_id}", status_code=status.HTTP_204_NO_CONTENT) async def delete_bank_transaction(transaction_id: str, db: AsyncSession = Depends(get_async_session)): """ 删除银行流水 """ logger.info(f"删除银行流水: {transaction_id}") query = select(BankTransaction).where(BankTransaction.transaction_id == transaction_id) result = await db.execute(query) transaction = result.scalar_one_or_none() if not transaction: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail=f"银行流水不存在: {transaction_id}", ) # 删除 await db.delete(transaction) await db.commit() return None