174 lines
6.2 KiB
Python
174 lines
6.2 KiB
Python
"""银行账户管理与交易查询后台路由。"""
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Path, Query
|
|
from sqlalchemy import select
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.dependencies import get_admin_user, get_db
|
|
from app.models.bank_account import BankAccount
|
|
from app.models.user import User
|
|
from app.services.bank.service import query_transactions_with_log
|
|
from app.utils.id_gen import generate_id
|
|
|
|
router = APIRouter(prefix="/admin/bank", tags=["admin-bank"])
|
|
|
|
|
|
# ============================================================
|
|
# 银行账户 CRUD
|
|
# ============================================================
|
|
|
|
|
|
@router.get("/accounts")
|
|
async def list_accounts(
|
|
admin: User = Depends(get_admin_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""列出所有银行账户。"""
|
|
result = await db.execute(select(BankAccount).order_by(BankAccount.is_default.desc(), BankAccount.created_at.desc()))
|
|
accounts = result.scalars().all()
|
|
return {"items": [
|
|
{
|
|
"id": a.id,
|
|
"account_name": a.account_name,
|
|
"bank_name": a.bank_name,
|
|
"account_no": a.account_no,
|
|
"is_active": a.is_active,
|
|
"is_default": a.is_default,
|
|
"description": a.description,
|
|
"created_at": a.created_at.isoformat() if a.created_at else None,
|
|
"updated_at": a.updated_at.isoformat() if a.updated_at else None,
|
|
}
|
|
for a in accounts
|
|
]}
|
|
|
|
|
|
@router.post("/accounts")
|
|
async def create_account(
|
|
body: dict,
|
|
admin: User = Depends(get_admin_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""新增银行账户。"""
|
|
account_name = (body.get("account_name") or "").strip()
|
|
bank_name = (body.get("bank_name") or "").strip()
|
|
account_no = (body.get("account_no") or "").strip()
|
|
if not account_name or not bank_name or not account_no:
|
|
raise HTTPException(status_code=400, detail="账户名称、开户银行、银行账号不能为空")
|
|
|
|
# 检查账号唯一性
|
|
existing = await db.execute(select(BankAccount).where(BankAccount.account_no == account_no))
|
|
if existing.scalar_one_or_none():
|
|
raise HTTPException(status_code=409, detail="该银行账号已存在")
|
|
|
|
is_default = bool(body.get("is_default", False))
|
|
# 如果设为默认,取消其他默认
|
|
if is_default:
|
|
await db.execute(
|
|
BankAccount.__table__.update().where(BankAccount.is_default.is_(True)).values(is_default=False)
|
|
)
|
|
|
|
account = BankAccount(
|
|
id=generate_id(),
|
|
account_name=account_name,
|
|
bank_name=bank_name,
|
|
account_no=account_no,
|
|
is_active=bool(body.get("is_active", True)),
|
|
is_default=is_default,
|
|
description=body.get("description"),
|
|
)
|
|
db.add(account)
|
|
await db.commit()
|
|
return {"id": account.id, "message": "创建成功"}
|
|
|
|
|
|
@router.put("/accounts/{account_id}")
|
|
async def update_account(
|
|
account_id: str = Path(..., description="账户 ID"),
|
|
body: dict = ...,
|
|
admin: User = Depends(get_admin_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""编辑银行账户。"""
|
|
result = await db.execute(select(BankAccount).where(BankAccount.id == account_id))
|
|
account = result.scalar_one_or_none()
|
|
if account is None:
|
|
raise HTTPException(status_code=404, detail="账户不存在")
|
|
|
|
if "account_name" in body:
|
|
account.account_name = str(body["account_name"]).strip()
|
|
if "bank_name" in body:
|
|
account.bank_name = str(body["bank_name"]).strip()
|
|
if "account_no" in body:
|
|
new_no = str(body["account_no"]).strip()
|
|
if new_no != account.account_no:
|
|
existing = await db.execute(select(BankAccount).where(BankAccount.account_no == new_no))
|
|
if existing.scalar_one_or_none():
|
|
raise HTTPException(status_code=409, detail="该银行账号已存在")
|
|
account.account_no = new_no
|
|
if "is_active" in body:
|
|
account.is_active = bool(body["is_active"])
|
|
if "description" in body:
|
|
account.description = body.get("description")
|
|
|
|
if body.get("is_default"):
|
|
await db.execute(
|
|
BankAccount.__table__.update()
|
|
.where(BankAccount.is_default.is_(True))
|
|
.where(BankAccount.id != account_id)
|
|
.values(is_default=False)
|
|
)
|
|
account.is_default = True
|
|
|
|
await db.commit()
|
|
return {"message": "更新成功"}
|
|
|
|
|
|
@router.delete("/accounts/{account_id}")
|
|
async def delete_account(
|
|
account_id: str = Path(..., description="账户 ID"),
|
|
admin: User = Depends(get_admin_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""删除银行账户。"""
|
|
result = await db.execute(select(BankAccount).where(BankAccount.id == account_id))
|
|
account = result.scalar_one_or_none()
|
|
if account is None:
|
|
raise HTTPException(status_code=404, detail="账户不存在")
|
|
await db.delete(account)
|
|
await db.commit()
|
|
return {"message": "删除成功"}
|
|
|
|
|
|
# ============================================================
|
|
# 银行交易查询
|
|
# ============================================================
|
|
|
|
|
|
@router.get("/transactions")
|
|
async def list_transactions(
|
|
account_id: str = Query(..., description="银行账户 ID"),
|
|
start_date: str = Query(..., description="开始日期 (YYYY-MM-DD)"),
|
|
end_date: str = Query(..., description="结束日期 (YYYY-MM-DD)"),
|
|
dc_flag: int | None = Query(None, description="借贷方向: 0-借/出金, 1-贷/入金, 不传返回全部"),
|
|
page: int = Query(1, ge=1, description="页码"),
|
|
page_size: int = Query(20, ge=1, le=100, description="每页数量"),
|
|
admin: User = Depends(get_admin_user),
|
|
db: AsyncSession = Depends(get_db),
|
|
):
|
|
"""查询银行交易流水(带接口请求记录到文件日志)。"""
|
|
result = await db.execute(select(BankAccount).where(BankAccount.id == account_id))
|
|
account = result.scalar_one_or_none()
|
|
if account is None:
|
|
raise HTTPException(status_code=404, detail="银行账户不存在")
|
|
|
|
data = await query_transactions_with_log(
|
|
admin.id,
|
|
acct_no=account.account_no,
|
|
start_date=start_date,
|
|
end_date=end_date,
|
|
dc_flag=dc_flag,
|
|
page=page,
|
|
page_size=page_size,
|
|
)
|
|
return data
|