From 25c2936039a8a6d74f08507a8d14ca224e0d9a5f Mon Sep 17 00:00:00 2001 From: aaron <> Date: Fri, 24 Jan 2025 22:23:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=93=B6=E8=A1=8C=E5=8D=A1?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E6=8E=A5=E5=8F=A3=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/api/endpoints/bank_card.py | 109 +++++++++++++++++++++++++++++++++ app/core/qcloud.py | 47 ++++++++++++++ app/main.py | 4 +- app/models/user_bank_card.py | 32 ++++++++++ 4 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 app/api/endpoints/bank_card.py create mode 100644 app/models/user_bank_card.py diff --git a/app/api/endpoints/bank_card.py b/app/api/endpoints/bank_card.py new file mode 100644 index 0000000..05e24a7 --- /dev/null +++ b/app/api/endpoints/bank_card.py @@ -0,0 +1,109 @@ +from fastapi import APIRouter, Depends +from sqlalchemy.orm import Session +from app.models.user_bank_card import UserBankCardDB, BankCardCreate, BankCardInfo +from app.models.user_auth import UserAuthDB +from app.models.database import get_db +from app.api.deps import get_current_user +from app.models.user import UserDB +from app.core.response import success_response, error_response, ResponseModel +from app.core.qcloud import qcloud_manager +from typing import List + +router = APIRouter() + +@router.post("", response_model=ResponseModel) +async def add_bank_card( + card: BankCardCreate, + db: Session = Depends(get_db), + current_user: UserDB = Depends(get_current_user) +): + """添加银行卡""" + # 检查是否已实名认证 + if not current_user.is_auth: + return error_response(code=400, message="请先完成实名认证") + + # 获取认证信息 + auth_info = db.query(UserAuthDB).filter( + UserAuthDB.user_id == current_user.userid + ).first() + + if not auth_info: + return error_response(code=400, message="未找到实名认证信息") + + # 验证姓名是否一致 + if card.name != auth_info.name: + return error_response(code=400, message="银行卡持卡人姓名与实名认证信息不一致") + + try: + # 调用银行卡三要素核验 + verify_result = await qcloud_manager.verify_bank_card( + name=auth_info.name, + id_card=auth_info.id_number, + card_number=card.card_number + ) + + # 验证不通过 + if verify_result["Result"] != "0": + return error_response( + code=400, + message=f"银行卡验证失败: {verify_result['Description']}" + ) + + # 创建银行卡记录 + bank_card = UserBankCardDB( + user_id=current_user.userid, + name=card.name, + card_number=card.card_number, + bank_name=card.bank_name + ) + db.add(bank_card) + db.commit() + db.refresh(bank_card) + + return success_response(data=BankCardInfo.model_validate(bank_card)) + except Exception as e: + db.rollback() + return error_response(code=500, message=f"添加银行卡失败: {str(e)}") + +@router.get("", response_model=ResponseModel) +async def get_bank_cards( + db: Session = Depends(get_db), + current_user: UserDB = Depends(get_current_user) +): + """获取用户的银行卡列表""" + cards = db.query(UserBankCardDB).filter( + UserBankCardDB.user_id == current_user.userid + ).all() + + # 对银行卡号进行脱敏处理 + card_list = [] + for card in cards: + card_info = BankCardInfo.model_validate(card) + # 只显示后四位,其他用*代替 + card_info.card_number = f"{'*' * (len(card.card_number)-4)}{card.card_number[-4:]}" + card_list.append(card_info) + + return success_response(data=card_list) + +@router.delete("/{card_id}", response_model=ResponseModel) +async def delete_bank_card( + card_id: int, + db: Session = Depends(get_db), + current_user: UserDB = Depends(get_current_user) +): + """删除银行卡""" + card = db.query(UserBankCardDB).filter( + UserBankCardDB.id == card_id, + UserBankCardDB.user_id == current_user.userid + ).first() + + if not card: + return error_response(code=404, message="银行卡不存在") + + try: + db.delete(card) + db.commit() + return success_response(message="删除成功") + except Exception as e: + db.rollback() + return error_response(code=500, message=f"删除失败: {str(e)}") \ No newline at end of file diff --git a/app/core/qcloud.py b/app/core/qcloud.py index 02b4258..ee8aa34 100644 --- a/app/core/qcloud.py +++ b/app/core/qcloud.py @@ -4,6 +4,7 @@ from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException from tencentcloud.faceid.v20180301 import faceid_client, models from tencentcloud.sms.v20210111 import sms_client, models as sms_models +from tencentcloud.faceid.v20180301 import models as faceid_models from qcloud_cos import CosConfig, CosS3Client from app.core.config import settings import json @@ -210,5 +211,51 @@ class QCloudManager: except Exception as e: raise Exception(f"获取上传URL失败: {str(e)}") + async def verify_bank_card(self, name: str, id_card: str, card_number: str) -> dict: + """ + 银行卡三要素核验 + + Args: + name: 姓名 + id_card: 身份证号 + card_number: 银行卡号 + + Returns: + dict: { + "Result": str, # "0":认证通过 "1":认证未通过 "2":认证不确定 + "Description": str, # 结果描述 + "RequestId": str # 请求ID + } + + Raises: + Exception: 验证失败时抛出异常 + """ + try: + self._init_faceid_client() + + # 构建请求 + req = faceid_models.BankCardVerificationRequest() + params = { + "Name": name, + "IdCard": id_card, + "BankCard": card_number + } + req.from_json_string(json.dumps(params)) + + # 发送请求 + response = self.faceid_client.BankCardVerification(req) + + # 解析结果 + result = json.loads(response.to_json_string()) + + return { + "Result": result.get("Result", "2"), + "Description": result.get("Description", "验证失败"), + "RequestId": result.get("RequestId", "") + } + + except TencentCloudSDKException as e: + raise Exception(f"银行卡三要素核验失败: {str(e)}") + # 创建全局实例 qcloud_manager = QCloudManager() \ No newline at end of file diff --git a/app/main.py b/app/main.py index 9f4f6ec..ae74a1f 100644 --- a/app/main.py +++ b/app/main.py @@ -1,6 +1,6 @@ from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware -from app.api.endpoints import wechat,user, address, community, station, order, coupon, community_building, upload, merchant, merchant_product, merchant_order, point, config, merchant_category, log, account,merchant_pay_order, message +from app.api.endpoints import wechat,user, address, community, station, order, coupon, community_building, upload, merchant, merchant_product, merchant_order, point, config, merchant_category, log, account,merchant_pay_order, message, bank_card from app.models.database import Base, engine from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse @@ -32,6 +32,7 @@ app.add_middleware(RequestLoggerMiddleware) # 添加用户路由 app.include_router(wechat.router,prefix="/api/wechat",tags=["微信"]) app.include_router(user.router, prefix="/api/user", tags=["用户"]) +app.include_router(bank_card.router, prefix="/api/bank-cards", tags=["用户银行卡"]) app.include_router(point.router, prefix="/api/point", tags=["用户积分"]) app.include_router(account.router, prefix="/api/account", tags=["账户"]) app.include_router(address.router, prefix="/api/address", tags=["配送地址"]) @@ -50,6 +51,7 @@ app.include_router(upload.router, prefix="/api/upload", tags=["文件上传"]) app.include_router(config.router, prefix="/api/config", tags=["系统配置"]) app.include_router(log.router, prefix="/api/logs", tags=["系统日志"]) + @app.get("/") async def root(): return {"message": "欢迎使用 Beefast 蜂快到家 API"} diff --git a/app/models/user_bank_card.py b/app/models/user_bank_card.py new file mode 100644 index 0000000..975923f --- /dev/null +++ b/app/models/user_bank_card.py @@ -0,0 +1,32 @@ +from sqlalchemy import Column, String, Integer, DateTime, ForeignKey +from sqlalchemy.sql import func +from pydantic import BaseModel, Field +from typing import Optional +from datetime import datetime +from .database import Base + +class UserBankCardDB(Base): + __tablename__ = "user_bank_cards" + + id = Column(Integer, primary_key=True, autoincrement=True) + user_id = Column(Integer, ForeignKey("users.userid"), nullable=False) + name = Column(String(50), nullable=False) + card_number = Column(String(30), nullable=False) + bank_name = Column(String(100), nullable=False) + create_time = Column(DateTime(timezone=True), server_default=func.now()) + update_time = Column(DateTime(timezone=True), onupdate=func.now()) + +class BankCardCreate(BaseModel): + name: str = Field(..., min_length=2, max_length=50) + card_number: str = Field(..., min_length=16, max_length=19, pattern="^[0-9]{16,19}$") + bank_name: str = Field(..., max_length=100) + +class BankCardInfo(BaseModel): + id: int + name: str + card_number: str + bank_name: str + create_time: datetime + + class Config: + from_attributes = True \ No newline at end of file