调整返回数据结构

This commit is contained in:
KingChan 2024-08-14 17:03:26 +08:00
parent 7e021a8700
commit 3d0a8c1c0a
15 changed files with 129 additions and 155 deletions

View File

@ -4,25 +4,22 @@
<option name="autoReloadType" value="SELECTIVE" /> <option name="autoReloadType" value="SELECTIVE" />
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="fc554a10-7837-4504-ac7d-04f725b153b1" name="Changes" comment="调整Database新增登录接口注册接口查询车站接口创建车站接口"> <list default="true" id="fc554a10-7837-4504-ac7d-04f725b153b1" name="Changes" comment="新增状态管理,调整返回数据结构">
<change afterPath="$PROJECT_DIR$/presenter/error.py" afterDir="false" /> <change afterPath="$PROJECT_DIR$/utils/response.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/presenter/mobile_code.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/utils/bank_server.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/utils/id_card_server.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/utils/state_code.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" /> <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/__init__.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/login_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/login_manager.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/login_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/login_manager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/mobile_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/mobile_manager.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/mobile_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/mobile_manager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/models/passenger_lib.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models/passenger_lib.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/passenger_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/passenger_manager.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/passenger_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/passenger_manager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/query_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/query_manager.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/app/query_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/query_manager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/ticket_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/ticket_manager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/presenter/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/__init__.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/presenter/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/__init__.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/presenter/presenter.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/presenter.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/presenter/error.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" /> <change beforePath="$PROJECT_DIR$/presenter/mobile_code.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/mobile_code.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/presenter/passenger.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/passenger.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/presenter/presenter.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/presenter/station.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/station.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/utils/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/utils/__init__.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/utils/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/utils/__init__.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/utils/mobile_server.py" beforeDir="false" afterPath="$PROJECT_DIR$/utils/mobile_server.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/utils/state_code.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/utils/redis_client.py" beforeDir="false" afterPath="$PROJECT_DIR$/utils/redis_client.py" afterDir="false" />
</list> </list>
<option name="SHOW_DIALOG" value="false" /> <option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" /> <option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -83,9 +80,9 @@
<recent name="D:\Mini12306_python" /> <recent name="D:\Mini12306_python" />
</key> </key>
<key name="MoveFile.RECENT_KEYS"> <key name="MoveFile.RECENT_KEYS">
<recent name="D:\Mini12306_python\utils" />
<recent name="D:\Mini12306_python\app" /> <recent name="D:\Mini12306_python\app" />
<recent name="D:\Mini12306_python" /> <recent name="D:\Mini12306_python" />
<recent name="D:\Mini12306_python\utils" />
<recent name="D:\Mini12306_python\jwt" /> <recent name="D:\Mini12306_python\jwt" />
<recent name="D:\Mini12306_python\models" /> <recent name="D:\Mini12306_python\models" />
</key> </key>
@ -127,7 +124,7 @@
<workItem from="1723423582377" duration="17938000" /> <workItem from="1723423582377" duration="17938000" />
<workItem from="1723468349162" duration="7753000" /> <workItem from="1723468349162" duration="7753000" />
<workItem from="1723509074827" duration="26219000" /> <workItem from="1723509074827" duration="26219000" />
<workItem from="1723596634243" duration="15471000" /> <workItem from="1723596634243" duration="19031000" />
</task> </task>
<task id="LOCAL-00001" summary="first init"> <task id="LOCAL-00001" summary="first init">
<option name="closed" value="true" /> <option name="closed" value="true" />
@ -177,7 +174,15 @@
<option name="project" value="LOCAL" /> <option name="project" value="LOCAL" />
<updated>1723543058473</updated> <updated>1723543058473</updated>
</task> </task>
<option name="localTasksCounter" value="7" /> <task id="LOCAL-00007" summary="新增状态管理,调整返回数据结构">
<option name="closed" value="true" />
<created>1723622681330</created>
<option name="number" value="00007" />
<option name="presentableId" value="LOCAL-00007" />
<option name="project" value="LOCAL" />
<updated>1723622681330</updated>
</task>
<option name="localTasksCounter" value="8" />
<servers /> <servers />
</component> </component>
<component name="TypeScriptGeneratedFilesManager"> <component name="TypeScriptGeneratedFilesManager">
@ -190,9 +195,20 @@
<MESSAGE value="databases" /> <MESSAGE value="databases" />
<MESSAGE value="databases init" /> <MESSAGE value="databases init" />
<MESSAGE value="调整Database新增登录接口注册接口查询车站接口创建车站接口" /> <MESSAGE value="调整Database新增登录接口注册接口查询车站接口创建车站接口" />
<option name="LAST_COMMIT_MESSAGE" value="调整Database新增登录接口注册接口查询车站接口创建车站接口" /> <MESSAGE value="新增状态管理,调整返回数据结构" />
<option name="LAST_COMMIT_MESSAGE" value="新增状态管理,调整返回数据结构" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
<breakpoints>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/presenter/__init__.py</url>
<option name="timeStamp" value="4" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component> </component>
<component name="com.intellij.coverage.CoverageDataManagerImpl"> <component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/Mini12306_python$Flask.coverage" NAME="Flask Coverage Results" MODIFIED="1723615977550" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" /> <SUITE FILE_PATH="coverage/Mini12306_python$Flask.coverage" NAME="Flask Coverage Results" MODIFIED="1723625998637" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component> </component>
</project> </project>

View File

@ -2,8 +2,8 @@
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from flask_jwt_extended import create_access_token from flask_jwt_extended import create_access_token
from app.models import Passenger from app.models import Passenger
from presenter import PassengerPresenter, ErrorPresenter from presenter import PassengerPresenter
from utils import StateCode from utils import StateCode, create_response
login_bp = Blueprint('login', __name__) login_bp = Blueprint('login', __name__)
@ -14,11 +14,11 @@ def login():
account = data.get('account') account = data.get('account')
password = data.get('password') password = data.get('password')
if not account or not password: if not account or not password:
return jsonify(ErrorPresenter().present(StateCode.PARAMS_ERROR)), 400 return jsonify(create_response(StateCode.PARAMS_ERROR)), 400
user = Passenger.query.filter_by(account=account).first() user = Passenger.query.filter_by(account=account).first()
if user and user.check_password(password): if user and user.check_password(password):
access_token = create_access_token(identity=user.id) access_token = create_access_token(identity=user.id)
user_presenter = PassengerPresenter(user, {"token": access_token}) user_presenter = PassengerPresenter(user, {"token": access_token}).as_dict()
return jsonify(user_presenter.present(StateCode.SUCCESS)), 200 return jsonify(create_response(StateCode.SUCCESS,single_data=user_presenter)), 200
return jsonify({'message': '登录失败,账号或密码错误'}), 401 return jsonify(create_response(StateCode.PASSWORD_INCORRECT)), 401

View File

@ -2,8 +2,8 @@ import pdb
from flask import Blueprint, jsonify, request from flask import Blueprint, jsonify, request
from presenter import ErrorPresenter, MobileCodePresenter from presenter import MobileCodePresenter
from utils import getVerificationCode, checkMobile, StateCode from utils import getVerificationCode, checkMobile,create_response, StateCode
mobile_bp = Blueprint('mobiles', __name__) mobile_bp = Blueprint('mobiles', __name__)
@ -13,7 +13,8 @@ mobile_bp = Blueprint('mobiles', __name__)
def getOtp(): def getOtp():
mobile_no = request.form.get('mobileNo') mobile_no = request.form.get('mobileNo')
if not checkMobile(mobile_no): if not checkMobile(mobile_no):
return jsonify(ErrorPresenter().present(StateCode.MOBILE_ERROR)), 400 return jsonify(create_response(StateCode.MOBILE_ERROR)), 400
sent_code = getVerificationCode(mobile_no) sent_code = getVerificationCode(mobile_no)
mobile_present = MobileCodePresenter(sent_code) mobile_present = MobileCodePresenter(sent_code).as_dict()
return jsonify(mobile_present.present(StateCode.SUCCESS)), 200 return jsonify(create_response(StateCode.SUCCESS, single_data=mobile_present)), 200

View File

@ -2,9 +2,8 @@ import pdb
from flask import Blueprint, request, jsonify from flask import Blueprint, request, jsonify
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
from app.models.passenger_lib import Passenger, isAvailable, createPassenger from app.models.passenger_lib import Passenger, isAvailable, createPassenger
from presenter import PassengerPresenter, ErrorPresenter from presenter import PassengerPresenter
from utils import verifyOpt, checkBankCard, checkIdCard from utils import StateCode, create_response, verifyOpt, checkIdCard, checkBankCard
from utils.state_code import StateCode
register_bp = Blueprint('register', __name__) register_bp = Blueprint('register', __name__)
@ -19,25 +18,25 @@ def register():
id_card_no = data.get('idCardNo') id_card_no = data.get('idCardNo')
bank_card_no = data.get('BankcardNo') bank_card_no = data.get('BankcardNo')
if not account or not password: if not account or not password:
return jsonify(ErrorPresenter().present(StateCode.PARAMS_ERROR)), 400 return jsonify(create_response(StateCode.PARAMS_ERROR)), 400
if not verifyOpt(mobile_no, mobile_code): if not verifyOpt(mobile_no, mobile_code):
return jsonify(ErrorPresenter().present(StateCode.MOBILE_CODE_ERROR)), 400 return jsonify(create_response(StateCode.MOBILE_CODE_ERROR)), 400
if not checkIdCard(id_card_no): if not checkIdCard(id_card_no):
return jsonify(ErrorPresenter().present(StateCode.ID_CARD_ERROR)), 400 return jsonify(create_response(StateCode.ID_CARD_ERROR)), 400
if isAvailable(account): if isAvailable(account):
return jsonify(ErrorPresenter().present(StateCode.USER_ALREADY_EXISTS)), 400 return jsonify(create_response(StateCode.USER_ALREADY_EXISTS)), 400
if not checkBankCard(bank_card_no): if not checkBankCard(bank_card_no):
return jsonify(ErrorPresenter().present(StateCode.BANK_CARD_ERROR)), 400 return jsonify(create_response(StateCode.BANK_CARD_ERROR)), 400
new_passenger_data = Passenger(data=data) new_passenger_data = Passenger(data=data)
new_passenger = createPassenger(new_passenger_data) new_passenger = createPassenger(new_passenger_data)
access_token = create_access_token(identity=new_passenger.id) access_token = create_access_token(identity=new_passenger.id)
user_presenter = PassengerPresenter(new_passenger, {"token": access_token}) user_presenter = PassengerPresenter(new_passenger, {"token": access_token}).as_dict()
return jsonify(user_presenter.present(StateCode.SUCCESS)), 200 return jsonify(create_response(StateCode.SUCCESS, single_data=user_presenter)), 200
@register_bp.route('/auth', methods=['GET']) @register_bp.route('/auth', methods=['GET'])
@ -45,5 +44,5 @@ def register():
def auth(): def auth():
current_user = get_jwt_identity() current_user = get_jwt_identity()
user = Passenger.query.get(current_user) user = Passenger.query.get(current_user)
user_presenter = PassengerPresenter(user) user_presenter = PassengerPresenter(user).as_dict()
return jsonify(user_presenter.present(StateCode.SUCCESS)), 200 return jsonify(create_response(code=StateCode.SUCCESS, single_data=user_presenter)), 200

View File

@ -5,7 +5,7 @@ from flask import Blueprint, jsonify, request
from app import db from app import db
from app.models import Station from app.models import Station
from presenter import StationPresenter from presenter import StationPresenter
from utils import StateCode from utils import create_response, StateCode
trains_bp = Blueprint('stations', __name__) trains_bp = Blueprint('stations', __name__)
@ -18,10 +18,8 @@ def query_train():
@trains_bp.route('/stations', methods=['GET']) @trains_bp.route('/stations', methods=['GET'])
def query_station(): def query_station():
stations = Station.query.all() stations = Station.query.all()
stations_presenters = [StationPresenter(station) for station in stations] stations_presenters = [StationPresenter(station).as_dict() for station in stations]
# 渲染所有车站为列表结构 return jsonify(create_response(StateCode.SUCCESS, multiple_data={"stations": stations_presenters})), 200
stations_data = [station.present() for station in stations_presenters]
return jsonify(stations_data)
@trains_bp.route('/stations', methods=['POST']) @trains_bp.route('/stations', methods=['POST'])
@ -30,5 +28,5 @@ def create_station():
new_station = Station(data=data) new_station = Station(data=data)
db.session.add(new_station) db.session.add(new_station)
db.session.commit() db.session.commit()
station_presenter = StationPresenter(new_station) station_presenter = StationPresenter(new_station).as_dict()
return jsonify(station_presenter.present(StateCode.SUCCESS)), 200 return jsonify(create_response(StateCode.SUCCESS, single_data=station_presenter)), 200

View File

@ -2,30 +2,11 @@ from flask import Blueprint, request, jsonify
from app.models.ticket_lib import Ticket from app.models.ticket_lib import Ticket
from app import db from app import db
from app.models.order_lib import Order from app.models.order_lib import Order
from utils.jwt import decode_jwt_token
order_bp = Blueprint('book_ticket', __name__) order_bp = Blueprint('book_ticket', __name__)
@order_bp.route('/book_ticket', methods=['POST']) @order_bp.route('/book_ticket', methods=['POST'])
def book_ticket(): def book_ticket():
token = request.headers.get('Authorization')
if not token:
return jsonify({'message': 'Token is missing'}), 401
user_id = decode_jwt_token(token)
if not user_id:
return jsonify({'message': 'Invalid or expired token'}), 401
data = request.json
train_id = data.get('train_id')
if not train_id:
return jsonify({'message': 'Train ID is required'}), 400
order = Order(passenger_id=user_id)
ticket = Ticket(train_id=train_id, order=order)
db.session.add(order)
db.session.add(ticket)
db.session.commit()
return jsonify({'message': 'Ticket booked successfully'}), 201 return jsonify({'message': 'Ticket booked successfully'}), 201

View File

@ -1,5 +1,3 @@
from .presenter import Presenter
from .passenger import PassengerPresenter from .passenger import PassengerPresenter
from .station import StationPresenter from .station import StationPresenter
from .error import ErrorPresenter
from .mobile_code import MobileCodePresenter from .mobile_code import MobileCodePresenter

View File

@ -1,11 +0,0 @@
from datetime import datetime
from presenter.presenter import Presenter
class ErrorPresenter(Presenter):
def __init__(self, aid_data=None):
super().__init__(aid_data)
def as_dict(self):
return None

View File

@ -1,11 +1,6 @@
from datetime import datetime class MobileCodePresenter():
def __init__(self, data):
from presenter.presenter import Presenter self.data = data
class MobileCodePresenter(Presenter):
def __init__(self, data, aid_data=None):
super().__init__(data, aid_data)
def as_dict(self): def as_dict(self):
return { return {

View File

@ -1,11 +1,8 @@
from datetime import datetime
from presenter.presenter import Presenter class PassengerPresenter:
def __init__(self, data, aid_data=None):
self.data = data
class PassengerPresenter(Presenter): self.aid_data = aid_data
def __init__(self, user, aid_data=None):
super().__init__(user, aid_data)
def as_dict(self): def as_dict(self):
return { return {
@ -16,5 +13,5 @@ class PassengerPresenter(Presenter):
"bankCardNo": self.data.bank_card_no, "bankCardNo": self.data.bank_card_no,
"state": self.data.state, "state": self.data.state,
"memberType": self.data.member_type "memberType": self.data.member_type
} } | self.aid_data

View File

@ -1,22 +0,0 @@
from sqlalchemy.orm import state
from utils import StateCode, STATE_MESSAGES
class Presenter:
def __init__(self, data, aid_data=None):
if aid_data is None:
aid_data = {}
self.data = data
self.aid_data = aid_data
def as_dict(self):
raise NotImplementedError("Subclasses must implement the as_dict method")
def present(self, code: StateCode):
"""返回统一格式的响应字典"""
return {
"code": code.value,
"message": STATE_MESSAGES.get(code, "Unknown error"),
"data": self.as_dict()
} | self.aid_data

View File

@ -1,11 +1,6 @@
from datetime import datetime class StationPresenter:
def __init__(self, data):
from presenter.presenter import Presenter self.data = data
class StationPresenter(Presenter):
def __init__(self, user, aid_data=None):
super().__init__(user, aid_data)
def as_dict(self): def as_dict(self):
return { return {

View File

@ -1,4 +1,4 @@
from .mobile_server import checkMobile, getVerificationCode, verifyOpt from .mobile_server import checkMobile, getVerificationCode, verifyOpt
from .id_card_server import checkIdCard from .id_card_server import checkIdCard
from .bank_server import checkBankCard from .bank_server import checkBankCard
from .state_code import StateCode, STATE_MESSAGES from .response import create_response, StateCode

56
utils/response.py Normal file
View File

@ -0,0 +1,56 @@
from enum import Enum
from typing import Any, Dict, List, Union
class StateCode(Enum):
SUCCESS = 0
NOT_AUTHORIZED = 1002
PARAMS_ERROR = 1003
USER_NOT_FOUND = 2001
USER_ALREADY_EXISTS = 2002
PASSWORD_INCORRECT = 2003
MOBILE_CODE_ERROR = 2004
ID_CARD_ERROR = 2005
BANK_CARD_ERROR = 2006
MOBILE_ERROR = 2007
STATE_MESSAGES = {
StateCode.SUCCESS: "OK",
StateCode.PARAMS_ERROR: "缺少参数",
StateCode.NOT_AUTHORIZED: "未登录",
StateCode.USER_NOT_FOUND: "用户不存在",
StateCode.USER_ALREADY_EXISTS: "用户已存在",
StateCode.PASSWORD_INCORRECT: "密码错误",
StateCode.MOBILE_CODE_ERROR: "验证码错误",
StateCode.ID_CARD_ERROR: "身份证号验证失败",
StateCode.MOBILE_ERROR: "手机号格式有误",
StateCode.BANK_CARD_ERROR: "银行卡验证错误"
}
def create_response(
code: StateCode,
message: str = None,
single_data: Dict[str, Any] = None,
multiple_data: Dict[str, List[Dict[str, Any]]] = None
) -> Dict[str, Any]:
"""
创建统一的返回格式
:param code: ErrorCode 错误码
:param message: 错误信息可选如果不传递则使用默认的错误信息
:param single_data: 包含单个 Presenter 转换后的数据字典
:param multiple_data: 包含多个 Presenter 转换后的数据字典键为描述值为对象列表
:return: 返回统一格式的字典
"""
return {
"code": code.value,
"msg": message or STATE_MESSAGES.get(code, "Unknown error"),
"data": {
**(single_data or {}),
**(multiple_data or {})
}
}

View File

@ -1,29 +0,0 @@
from enum import Enum
class StateCode(Enum):
SUCCESS = 0
NOT_AUTHORIZED = 1002
PARAMS_ERROR = 1003
USER_NOT_FOUND = 2001
USER_ALREADY_EXISTS = 2002
PASSWORD_INCORRECT = 2003
MOBILE_CODE_ERROR = 2004
ID_CARD_ERROR = 2005
BANK_CARD_ERROR = 2006
MOBILE_ERROR = 2007
STATE_MESSAGES = {
StateCode.SUCCESS: "OK",
StateCode.PARAMS_ERROR: "缺少参数",
StateCode.NOT_AUTHORIZED: "未登录",
StateCode.USER_NOT_FOUND: "用户不存在",
StateCode.USER_ALREADY_EXISTS: "用户已存在",
StateCode.PASSWORD_INCORRECT: "密码错误",
StateCode.MOBILE_CODE_ERROR: "验证码错误",
StateCode.ID_CARD_ERROR: "身份证号验证失败",
StateCode.MOBILE_ERROR: "手机号格式有误",
StateCode.BANK_CARD_ERROR: "银行卡验证错误"
}