1、后台菜单增加定时任务和银行交易
2、增加后台配置银行账户和请求网址
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
from app.services.bank.client import query_bank_transactions, BankApiError
|
||||
from app.services.bank.service import query_transactions_with_log
|
||||
|
||||
__all__ = ["query_bank_transactions", "BankApiError", "query_transactions_with_log"]
|
||||
@@ -0,0 +1,76 @@
|
||||
"""银行交易查询 API 客户端。"""
|
||||
|
||||
import httpx
|
||||
|
||||
from app.config import settings
|
||||
|
||||
|
||||
class BankApiError(RuntimeError):
|
||||
"""银行接口错误。"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
message: str,
|
||||
retryable: bool = False,
|
||||
http_status: int | None = None,
|
||||
):
|
||||
super().__init__(message)
|
||||
self.retryable = retryable
|
||||
self.http_status = http_status
|
||||
|
||||
|
||||
async def query_bank_transactions(
|
||||
acct_no: str,
|
||||
start_date: str,
|
||||
end_date: str,
|
||||
dc_flag: int | None = None,
|
||||
page: int = 1,
|
||||
page_size: int = 20,
|
||||
) -> dict:
|
||||
"""查询银行交易流水。
|
||||
|
||||
调用外部银行接口,按账号和日期范围查询交易记录。
|
||||
|
||||
:param acct_no: 我方银行账号
|
||||
:param start_date: 开始日期 (YYYY-MM-DD)
|
||||
:param end_date: 结束日期 (YYYY-MM-DD)
|
||||
:param dc_flag: 借贷方向 (0-借/出金, 1-贷/入金, None-全部)
|
||||
:param page: 页码
|
||||
:param page_size: 每页数量 (最大 100)
|
||||
:return: 接口返回的原始数据
|
||||
"""
|
||||
base = (settings.BANK_API_BASE or "").rstrip("/")
|
||||
if not base:
|
||||
raise BankApiError("银行接口地址未配置 (BANK_API_BASE)", retryable=False)
|
||||
|
||||
url = f"{base}/api/v1/internal/get-blank-transfer-acct-time"
|
||||
headers = {
|
||||
"api-key": settings.BANK_API_KEY or "",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
payload: dict = {
|
||||
"acct_no": acct_no,
|
||||
"start_date": start_date,
|
||||
"end_date": end_date,
|
||||
"page": page,
|
||||
"page_size": min(page_size, 100),
|
||||
}
|
||||
if dc_flag is not None:
|
||||
payload["dc_flag"] = dc_flag
|
||||
|
||||
timeout = httpx.Timeout(30.0)
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=timeout, follow_redirects=True) as client:
|
||||
response = await client.post(url, headers=headers, json=payload)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
raise BankApiError(
|
||||
f"银行接口返回错误: HTTP {e.response.status_code}",
|
||||
retryable=e.response.status_code >= 500,
|
||||
http_status=e.response.status_code,
|
||||
) from e
|
||||
except httpx.TimeoutException as e:
|
||||
raise BankApiError("银行接口请求超时", retryable=True) from e
|
||||
except httpx.RequestError as e:
|
||||
raise BankApiError(f"银行接口请求失败: {e}", retryable=True) from e
|
||||
@@ -0,0 +1,50 @@
|
||||
"""银行接口请求文件日志记录器。
|
||||
|
||||
使用 get_logger 模式,日志输出到 log/bank_api/bank_api-YYYY-MM-DD.log。
|
||||
"""
|
||||
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
|
||||
from app.utils.logger import get_logger
|
||||
|
||||
bank_api_logger = get_logger("bank_api", "bank_api")
|
||||
|
||||
|
||||
def log_bank_api_request(
|
||||
acct_no: str,
|
||||
request_params: dict | None,
|
||||
response_data: dict | None,
|
||||
status_code: int | None,
|
||||
is_success: bool,
|
||||
error_msg: str | None = None,
|
||||
admin_id: str | None = None,
|
||||
duration_ms: int | None = None,
|
||||
) -> None:
|
||||
"""记录银行接口请求到日志文件。
|
||||
|
||||
:param acct_no: 查询的银行账号
|
||||
:param request_params: 请求参数
|
||||
:param response_data: 响应数据
|
||||
:param status_code: HTTP 状态码
|
||||
:param is_success: 是否成功
|
||||
:param error_msg: 错误信息
|
||||
:param admin_id: 操作管理员 ID
|
||||
:param duration_ms: 请求耗时(毫秒)
|
||||
"""
|
||||
log_data = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"acct_no": acct_no,
|
||||
"request_params": request_params,
|
||||
"response_data": response_data,
|
||||
"status_code": status_code,
|
||||
"is_success": is_success,
|
||||
"error_msg": error_msg,
|
||||
"admin_id": admin_id,
|
||||
"duration_ms": duration_ms,
|
||||
}
|
||||
line = json.dumps(log_data, ensure_ascii=False, default=str)
|
||||
if is_success:
|
||||
bank_api_logger.info(line)
|
||||
else:
|
||||
bank_api_logger.error(line)
|
||||
@@ -0,0 +1,47 @@
|
||||
"""银行交易查询服务 — 接口请求日志写入文件。"""
|
||||
|
||||
import time
|
||||
|
||||
from app.services.bank.client import query_bank_transactions, BankApiError
|
||||
from app.services.bank.file_logger import log_bank_api_request
|
||||
|
||||
|
||||
async def query_transactions_with_log(
|
||||
admin_id: str,
|
||||
**kwargs,
|
||||
) -> dict:
|
||||
"""查询银行流水并记录接口日志到文件。
|
||||
|
||||
:param admin_id: 操作管理员 ID
|
||||
:param kwargs: 透传给 query_bank_transactions 的参数
|
||||
:return: 接口返回的原始数据
|
||||
"""
|
||||
acct_no = kwargs.get("acct_no", "")
|
||||
request_params = {k: v for k, v in kwargs.items()}
|
||||
start_time = time.monotonic()
|
||||
try:
|
||||
result = await query_bank_transactions(**kwargs)
|
||||
duration_ms = int((time.monotonic() - start_time) * 1000)
|
||||
log_bank_api_request(
|
||||
acct_no=acct_no,
|
||||
request_params=request_params,
|
||||
response_data=result,
|
||||
status_code=200,
|
||||
is_success=True,
|
||||
admin_id=admin_id,
|
||||
duration_ms=duration_ms,
|
||||
)
|
||||
return result
|
||||
except BankApiError as e:
|
||||
duration_ms = int((time.monotonic() - start_time) * 1000)
|
||||
log_bank_api_request(
|
||||
acct_no=acct_no,
|
||||
request_params=request_params,
|
||||
response_data=None,
|
||||
status_code=e.http_status,
|
||||
is_success=False,
|
||||
error_msg=str(e),
|
||||
admin_id=admin_id,
|
||||
duration_ms=duration_ms,
|
||||
)
|
||||
raise
|
||||
Reference in New Issue
Block a user