车次查询接口

This commit is contained in:
KingChan 2024-08-15 16:41:25 +08:00
parent c188687dbb
commit 62c4bc9019
13 changed files with 284 additions and 89 deletions

View File

@ -4,15 +4,23 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="fc554a10-7837-4504-ac7d-04f725b153b1" name="Changes" comment="调整返回数据结构">
<list default="true" id="fc554a10-7837-4504-ac7d-04f725b153b1" name="Changes" comment="station管理">
<change afterPath="$PROJECT_DIR$/app/train_manager.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/presenter/train.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/presenter/train_station.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" 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/models/passenger_lib.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models/passenger_lib.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/__init__.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/models/station_lib.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models/station_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/models/train_lib.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models/train_lib.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/models/train_station_lib.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/models/train_station_lib.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$/presenter/station.py" beforeDir="false" afterPath="$PROJECT_DIR$/presenter/station.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/utils/response.py" beforeDir="false" afterPath="$PROJECT_DIR$/utils/response.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app/station_manager.py" beforeDir="false" afterPath="$PROJECT_DIR$/app/station_manager.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/migrations/README" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/migrations/alembic.ini" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/migrations/env.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/migrations/script.py.mako" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/migrations/versions/a443e9478ede_init_dabases.py" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/requirements.txt" beforeDir="false" afterPath="$PROJECT_DIR$/requirements.txt" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@ -49,27 +57,27 @@
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent">{
&quot;keyToString&quot;: {
&quot;Flask server.Flask.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;master&quot;,
&quot;ignore.virus.scanning.warn.message&quot;: &quot;true&quot;,
&quot;last_opened_file_path&quot;: &quot;D:/Mini12306_python/presenter&quot;,
&quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
&quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
&quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
&quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
&quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
&quot;settings.editor.selected.configurable&quot;: &quot;http.proxy&quot;,
&quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"Flask server.Flask.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"git-widget-placeholder": "master",
"ignore.virus.scanning.warn.message": "true",
"last_opened_file_path": "D:/Mini12306_python/presenter",
"node.js.detected.package.eslint": "true",
"node.js.detected.package.tslint": "true",
"node.js.selected.package.eslint": "(autodetect)",
"node.js.selected.package.tslint": "(autodetect)",
"nodejs_package_manager_path": "npm",
"settings.editor.selected.configurable": "http.proxy",
"vue.rearranger.settings.migration": "true"
}
}</component>
}]]></component>
<component name="RecentsManager">
<key name="CopyFile.RECENT_KEYS">
<recent name="D:\Mini12306_python\presenter" />
<recent name="D:\Mini12306_python\models" />
<recent name="D:\Mini12306_python\app" />
<recent name="D:\Mini12306_python\models" />
<recent name="D:\Mini12306_python" />
</key>
<key name="MoveFile.RECENT_KEYS">
@ -118,7 +126,7 @@
<workItem from="1723468349162" duration="7753000" />
<workItem from="1723509074827" duration="26219000" />
<workItem from="1723596634243" duration="20869000" />
<workItem from="1723686587261" duration="5136000" />
<workItem from="1723686587261" duration="16430000" />
</task>
<task id="LOCAL-00001" summary="first init">
<option name="closed" value="true" />
@ -192,7 +200,15 @@
<option name="project" value="LOCAL" />
<updated>1723627176588</updated>
</task>
<option name="localTasksCounter" value="10" />
<task id="LOCAL-00010" summary="station管理">
<option name="closed" value="true" />
<created>1723692519788</created>
<option name="number" value="00010" />
<option name="presentableId" value="LOCAL-00010" />
<option name="project" value="LOCAL" />
<updated>1723692519789</updated>
</task>
<option name="localTasksCounter" value="11" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@ -207,7 +223,8 @@
<MESSAGE value="调整Database新增登录接口注册接口查询车站接口创建车站接口" />
<MESSAGE value="新增状态管理,调整返回数据结构" />
<MESSAGE value="调整返回数据结构" />
<option name="LAST_COMMIT_MESSAGE" value="调整返回数据结构" />
<MESSAGE value="station管理" />
<option name="LAST_COMMIT_MESSAGE" value="station管理" />
</component>
<component name="XDebuggerManager">
<breakpoint-manager>
@ -221,10 +238,15 @@
<line>7</line>
<option name="timeStamp" value="5" />
</line-breakpoint>
<line-breakpoint enabled="true" suspend="THREAD" type="python-line">
<url>file://$PROJECT_DIR$/app/station_manager.py</url>
<line>12</line>
<option name="timeStamp" value="6" />
</line-breakpoint>
</breakpoints>
</breakpoint-manager>
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/Mini12306_python$Flask.coverage" NAME="Flask Coverage Results" MODIFIED="1723690688101" 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="1723709685990" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
</component>
</project>

View File

@ -18,12 +18,16 @@ def create_app():
# routes
from app.login_manager import login_bp
from app.passenger_manager import register_bp
from app.query_manager import trains_bp
from app.query_manager import query_bp
from app.station_manager import station_bp
from app.mobile_manager import mobile_bp
from app.train_manager import trains_bp
app.register_blueprint(login_bp)
app.register_blueprint(register_bp)
app.register_blueprint(trains_bp)
app.register_blueprint(query_bp)
app.register_blueprint(station_bp)
app.register_blueprint(mobile_bp)
app.register_blueprint(trains_bp)
return app

View File

@ -9,7 +9,7 @@ class Station(db.Model):
__tablename__: str = 'station'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(120), index=True)
name = db.Column(db.String(120), unique=True, nullable=False, index=True)
pinyin = db.Column(db.String(120))
province = db.Column(db.String(120))
city = db.Column(db.String(120))

View File

@ -1,6 +1,5 @@
from app.models import db
class Train(db.Model):
__tablename__: str = 'train'
@ -9,9 +8,17 @@ class Train(db.Model):
departure_station = db.Column(db.String(120))
arrival_station = db.Column(db.String(120))
departure_time = db.Column(db.DateTime)
expiration_time = db.Column(db.DateTime)
effective_time = db.Column(db.DateTime)
arrival_time = db.Column(db.DateTime)
created_at = db.Column(db.DateTime)
updated_at = db.Column(db.DateTime)
def __repr__(self):
return f'<Train {self.id}>'
@classmethod
def create(cls, new_train):
db.session.add(new_train)
db.session.commit()
return new_train

View File

@ -4,16 +4,16 @@ from app.models import db
class TrainStation(db.Model):
__tablename__: str = 'train_station'
id = db.Column(db.Integer, primary_key=True)
train_no = db.Column(db.String(120), index=True)
station_name = db.Column(db.String(120))
train_no = db.Column(db.String(120), db.ForeignKey('train.train_no'), primary_key=True, index=True)
station_name = db.Column(db.String(120), db.ForeignKey('station.name'), primary_key=True)
price = db.Column(db.Numeric(8, 2))
arrival_station = db.Column(db.String(120))
arrival_time = db.Column(db.DateTime)
departure_time = db.Column(db.DateTime)
index = db.Column(db.Integer, default=1)
index = db.Column(db.Integer, default=0)
created_at = db.Column(db.DateTime)
updated_at = db.Column(db.DateTime)
def __repr__(self):
return f'<TrainStation {self.id}>'
station = db.relationship('Station', backref=db.backref('train_stations'))
train = db.relationship('Train', backref=db.backref('train_stations'))

View File

@ -1,39 +1,54 @@
import pdb
from datetime import datetime
from dateutil import parser
from flask import Blueprint, jsonify, request
from app import db
from app.models import Station
from presenter import StationPresenter
from app.models import Train, TrainStation
from presenter.train import TrainPresenter
from utils import create_response, StateCode
trains_bp = Blueprint('stations', __name__)
query_bp = Blueprint('query', __name__)
@trains_bp.route('/trains/query_train', methods=['GET'])
@query_bp.route('/trains/query_train', methods=['GET'])
def query_train():
return jsonify({'message': 'Ticket booked successfully'}), 201
from_station = request.args.get('from')
to_station = request.args.get('to')
date = datetime.strptime(request.args.get('date'), "%Y-%m-%dT%H:%M:%S")
trains = query_trains_by_date_and_nos(from_station, to_station, date)
# trains = Train.query.all()
trains_presenters = [TrainPresenter(train).as_dict() for train in trains]
return jsonify(create_response(StateCode.SUCCESS, data=trains_presenters)), 200
@trains_bp.route('/stations', methods=['GET'])
def query_station():
stations = Station.query.all()
stations_presenters = [StationPresenter(station).as_dict() for station in stations]
return jsonify(create_response(StateCode.SUCCESS, data=stations_presenters)), 200
def query_trains_by_date_and_nos(from_station, to_station, date):
# Query for train stations where the station name matches `from_station`
from_train = TrainStation.query.filter_by(station_name=from_station).all()
# Query for train stations where the station name matches `to_station`
to_train = TrainStation.query.filter_by(station_name=to_station).all()
@trains_bp.route('/stations', methods=['POST'])
def create_station():
data = request.form
new_station = Station.create(data=data)
station_presenter = StationPresenter(new_station).as_dict()
return jsonify(create_response(StateCode.SUCCESS, data=station_presenter)), 200
# Extract train_no from both query results
from_train_nos = {ts.train_no for ts in from_train}
to_train_nos = {ts.train_no for ts in to_train}
# Find the common train_no between the two stations
common_train_nos = from_train_nos & to_train_nos
@trains_bp.route('/stations/quantity_create', methods=['POST'])
def quantity_create():
stations = request.json.get("stations")
for name in stations:
station_hash = {"name": name}
Station.create(station_hash)
return jsonify(create_response(StateCode.SUCCESS)), 200
# Filter train numbers where the index of the from station is less than the index of the to station
valid_train_nos = [
train_no for train_no in common_train_nos
if next(ts.index for ts in from_train if ts.train_no == train_no) <
next(ts.index for ts in to_train if ts.train_no == train_no)
]
# Query trains by the filtered train numbers and the given date (assuming date filtering)
trains = Train.query.filter(
Train.effective_time >= date,
Train.train_no.in_(valid_train_nos)
).all()
# Assuming you have a presenter or serializer for the trains
return trains

View File

@ -1,10 +1,32 @@
import pdb
from flask import request, jsonify
from flask import request, jsonify, Blueprint
from app import db
from app.models import Station
from app.query_manager import trains_bp
from presenter import StationPresenter
from utils import create_response, StateCode
station_bp = Blueprint('stations', __name__)
@station_bp.route('/stations', methods=['GET'])
def query_station():
stations = Station.query.all()
stations_presenters = [StationPresenter(station).as_dict() for station in stations]
return jsonify(create_response(StateCode.SUCCESS, data=stations_presenters)), 200
@station_bp.route('/stations', methods=['POST'])
def create_station():
data = request.form
new_station = Station.create(data=data)
station_presenter = StationPresenter(new_station).as_dict()
return jsonify(create_response(StateCode.SUCCESS, data=station_presenter)), 200
@station_bp.route('/stations/quantity_create', methods=['POST'])
def quantity_create():
stations = request.json.get("stations")
for name in stations:
station_hash = {"name": name}
Station.create(station_hash)
return jsonify(create_response(StateCode.SUCCESS)), 200

59
app/train_manager.py Normal file
View File

@ -0,0 +1,59 @@
import pdb
from flask import request, jsonify, Blueprint
from app import db
from app.models import Train, TrainStation
from presenter.train import TrainPresenter
from utils import create_response, StateCode
trains_bp = Blueprint('trains', __name__)
@trains_bp.route('/trains', methods=['GET'])
def query_station():
return jsonify(create_response(StateCode.SUCCESS)), 200
@trains_bp.route('/trains', methods=['POST'])
def create_station():
data = request.json
new_train = build_train(data)
db.session.add(new_train)
db.session.commit()
train_presenter = TrainPresenter(new_train).as_dict()
return jsonify(create_response(StateCode.SUCCESS, data=train_presenter)), 200
def build_train(params):
# Create a new Train object
train = Train(
train_no=params['trainNo'],
effective_time=params['effective_time'],
expiration_time=params['expiration_time']
)
# Extract indexes for determining first and last station
indexes = [e["index"] for e in params['stations']]
for e in params['stations']:
# Create and associate TrainStation objects
train_station = TrainStation(
station_name=e["name"],
price=e["price"],
departure_time=e["depTime"],
arrival_time=e["arrTime"],
index=e["index"]
)
train.train_stations.append(train_station)
# Determine the departure time and station for the first station
if e["index"] == 0:
train.departure_time = e["depTime"]
train.departure_station = e["name"]
# Determine the arrival time and station for the last station
if e["index"] == max(indexes):
train.arrival_time = e["arrTime"]
train.arrival_station = e["name"]
return train

View File

@ -1,8 +1,8 @@
"""init dabases
"""init databases
Revision ID: a443e9478ede
Revision ID: 1b3bc6809b30
Revises:
Create Date: 2024-08-13 11:53:04.942790
Create Date: 2024-08-15 15:09:26.124279
"""
from alembic import op
@ -10,7 +10,7 @@ import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = 'a443e9478ede'
revision = '1b3bc6809b30'
down_revision = None
branch_labels = None
depends_on = None
@ -56,7 +56,7 @@ def upgrade():
op.create_table('station',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('name', sa.String(length=120), nullable=True),
sa.Column('name', sa.String(length=120), nullable=False),
sa.Column('pinyin', sa.String(length=120), nullable=True),
sa.Column('province', sa.String(length=120), nullable=True),
sa.Column('city', sa.String(length=120), nullable=True),
@ -66,7 +66,7 @@ def upgrade():
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('station', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_station_name'), ['name'], unique=False)
batch_op.create_index(batch_op.f('ix_station_name'), ['name'], unique=True)
op.create_table('train',
sa.Column('id', sa.Integer(), nullable=False),
@ -74,6 +74,8 @@ def upgrade():
sa.Column('departure_station', sa.String(length=120), nullable=True),
sa.Column('arrival_station', sa.String(length=120), nullable=True),
sa.Column('departure_time', sa.DateTime(), nullable=True),
sa.Column('expiration_time', sa.DateTime(), nullable=True),
sa.Column('effective_time', sa.DateTime(), nullable=True),
sa.Column('arrival_time', sa.DateTime(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
@ -82,21 +84,6 @@ def upgrade():
with op.batch_alter_table('train', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_train_train_no'), ['train_no'], unique=True)
op.create_table('train_station',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('train_no', sa.String(length=120), nullable=True),
sa.Column('station_name', sa.String(length=120), nullable=True),
sa.Column('price', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('arrival_station', sa.String(length=120), nullable=True),
sa.Column('departure_time', sa.DateTime(), nullable=True),
sa.Column('index', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.PrimaryKeyConstraint('id')
)
with op.batch_alter_table('train_station', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_train_station_train_no'), ['train_no'], unique=False)
op.create_table('order',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('order_no', sa.String(length=120), nullable=False),
@ -113,6 +100,23 @@ def upgrade():
with op.batch_alter_table('order', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_order_passenger_id'), ['passenger_id'], unique=False)
op.create_table('train_station',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('train_no', sa.String(length=120), nullable=False),
sa.Column('station_name', sa.String(length=120), nullable=False),
sa.Column('price', sa.Numeric(precision=8, scale=2), nullable=True),
sa.Column('arrival_time', sa.DateTime(), nullable=True),
sa.Column('departure_time', sa.DateTime(), nullable=True),
sa.Column('index', sa.Integer(), nullable=True),
sa.Column('created_at', sa.DateTime(), nullable=True),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.ForeignKeyConstraint(['station_name'], ['station.name'], ),
sa.ForeignKeyConstraint(['train_no'], ['train.train_no'], ),
sa.PrimaryKeyConstraint('id', 'train_no', 'station_name')
)
with op.batch_alter_table('train_station', schema=None) as batch_op:
batch_op.create_index(batch_op.f('ix_train_station_train_no'), ['train_no'], unique=False)
op.create_table('ticket',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('seat_no', sa.String(length=120), nullable=True),
@ -150,14 +154,14 @@ def downgrade():
batch_op.drop_index(batch_op.f('ix_ticket_order_id'))
op.drop_table('ticket')
with op.batch_alter_table('order', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_order_passenger_id'))
op.drop_table('order')
with op.batch_alter_table('train_station', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_train_station_train_no'))
op.drop_table('train_station')
with op.batch_alter_table('order', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_order_passenger_id'))
op.drop_table('order')
with op.batch_alter_table('train', schema=None) as batch_op:
batch_op.drop_index(batch_op.f('ix_train_train_no'))

View File

@ -0,0 +1,32 @@
"""remove TrainStation id
Revision ID: 7866a6e8a75b
Revises: 1b3bc6809b30
Create Date: 2024-08-15 15:29:40.410360
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '7866a6e8a75b'
down_revision = '1b3bc6809b30'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('train_station', schema=None) as batch_op:
batch_op.drop_column('id')
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
with op.batch_alter_table('train_station', schema=None) as batch_op:
batch_op.add_column(sa.Column('id', sa.INTEGER(), autoincrement=False, nullable=False))
# ### end Alembic commands ###

16
presenter/train.py Normal file
View File

@ -0,0 +1,16 @@
from presenter.train_station import TrainStationPresenter
class TrainPresenter:
def __init__(self, data):
self.data = data
def as_dict(self):
return {
"id": self.data.id,
"trainNo": self.data.train_no,
"expiration_time": self.data.expiration_time,
"effective_time": self.data.effective_time,
"train_stations": [TrainStationPresenter(station).as_dict() for station in self.data.train_stations] ,
}

View File

@ -0,0 +1,13 @@
class TrainStationPresenter:
def __init__(self, data):
self.data = data
def as_dict(self):
return {
"index": self.data.index,
"name": self.data.station_name,
"arrTime": self.data.arrival_time,
"depTime": self.data.departure_time,
"price": self.data.price,
}

View File

@ -9,4 +9,5 @@ Flask-JWT-Extended
pypinyin~=0.52.0
Werkzeug~=3.0.3
SQLAlchemy~=2.0.32
alembic~=1.13.2
alembic~=1.13.2
python-dateutil