1. 项目背景与需求分析
校园电动车短租平台是一个针对高校场景设计的共享出行解决方案。随着校园面积不断扩大,师生日常通勤需求日益增长,传统的步行或自行车已经无法满足部分场景下的高效移动需求。我们团队在调研中发现,许多高校存在以下痛点:
- 教学楼、宿舍区、实验室分布分散,课间转场时间紧张
- 校外实习学生往返校园交通不便
- 节假日校园游览缺乏便捷交通工具
- 个人购买电动车存在充电难、维护成本高的问题
基于这些观察,我们决定开发一个基于Python+Flask的校园电动车短租平台。选择这套技术栈主要基于以下考虑:
- Python语言开发效率高,适合快速迭代
- Flask框架轻量灵活,便于定制业务逻辑
- Vue.js前端框架提供良好的用户交互体验
- PyCharm作为专业Python IDE可提升开发效率
- Django部分组件可复用(如ORM、Admin)
2. 技术架构设计
2.1 整体架构
平台采用前后端分离架构,主要分为三个层次:
code复制前端展示层(Vue.js)
│
API接口层(Flask RESTful)
│
数据服务层(MySQL + Redis)
2.2 技术选型对比
我们对比了Flask和Django两个主流Python Web框架:
| 特性 | Flask | Django |
|---|---|---|
| 适用场景 | 中小型项目,API服务 | 大型全功能项目 |
| 灵活性 | 高 | 中等 |
| 内置功能 | 基础路由和模板 | 完整MVC框架 |
| 学习曲线 | 平缓 | 较陡峭 |
| 性能 | 较高 | 中等 |
| 扩展性 | 通过插件扩展 | 内置丰富功能 |
最终选择Flask是因为:
- 项目初期需要快速迭代验证商业模式
- 业务逻辑相对简单,不需要Django的全套功能
- 团队成员对Flask更熟悉
2.3 数据库设计
核心数据表包括:
python复制class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
student_id = db.Column(db.String(20), unique=True)
password = db.Column(db.String(128))
balance = db.Column(db.Float, default=0.0)
class Vehicle(db.Model):
id = db.Column(db.Integer, primary_key=True)
plate_number = db.Column(db.String(10), unique=True)
battery_level = db.Column(db.Integer)
status = db.Column(db.String(20)) # available, rented, maintenance
class Order(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
vehicle_id = db.Column(db.Integer, db.ForeignKey('vehicle.id'))
start_time = db.Column(db.DateTime)
end_time = db.Column(db.DateTime)
total_fee = db.Column(db.Float)
3. 核心功能实现
3.1 用户认证模块
采用JWT(JSON Web Token)实现无状态认证:
python复制from flask_jwt_extended import create_access_token, jwt_required
@app.route('/login', methods=['POST'])
def login():
username = request.json.get('username')
password = request.json.get('password')
user = User.query.filter_by(student_id=username).first()
if user and check_password_hash(user.password, password):
access_token = create_access_token(identity=username)
return jsonify(access_token=access_token)
else:
return jsonify({"msg": "Bad credentials"}), 401
3.2 车辆租赁流程
- 用户通过地图界面查看可用车辆
- 扫码或点击预约目标车辆
- 系统锁定车辆15分钟(防止占位)
- 用户到达车辆位置,蓝牙解锁
- 开始计费,每分钟0.3元
- 用户到达目的地后,点击结束行程
- 系统计算费用并从余额扣除
关键代码实现:
python复制@app.route('/rent', methods=['POST'])
@jwt_required()
def rent_vehicle():
current_user = get_jwt_identity()
vehicle_id = request.json.get('vehicle_id')
# 检查用户余额
user = User.query.filter_by(student_id=current_user).first()
if user.balance < 10: # 最低余额要求
return jsonify({"msg": "Insufficient balance"}), 400
# 检查车辆状态
vehicle = Vehicle.query.get(vehicle_id)
if vehicle.status != 'available':
return jsonify({"msg": "Vehicle not available"}), 400
# 创建订单
new_order = Order(
user_id=user.id,
vehicle_id=vehicle.id,
start_time=datetime.now(),
status='ongoing'
)
db.session.add(new_order)
# 更新车辆状态
vehicle.status = 'rented'
db.session.commit()
return jsonify({"msg": "Rent success"})
3.3 支付系统集成
与校园一卡通系统对接,实现余额充值:
python复制@app.route('/recharge', methods=['POST'])
@jwt_required()
def recharge():
current_user = get_jwt_identity()
amount = request.json.get('amount')
# 调用校园支付接口
response = requests.post(
'https://campus-pay/api/recharge',
json={'student_id': current_user, 'amount': amount},
headers={'Authorization': 'Bearer ' + CAMPUS_PAY_KEY}
)
if response.status_code == 200:
user = User.query.filter_by(student_id=current_user).first()
user.balance += amount
db.session.commit()
return jsonify({"msg": "Recharge success"})
else:
return jsonify({"msg": "Recharge failed"}), 400
4. 前端Vue实现
4.1 地图组件集成
使用高德地图API实现车辆位置展示:
javascript复制<template>
<div class="map-container">
<amap
:center="center"
:zoom="15"
@init="initMap"
>
<amap-marker
v-for="vehicle in vehicles"
:key="vehicle.id"
:position="[vehicle.lng, vehicle.lat]"
@click="showVehicleInfo(vehicle)"
/>
</amap>
</div>
</template>
<script>
import { AMap, AMapMarker } from 'vue-amap'
export default {
components: { AMap, AMapMarker },
data() {
return {
center: [116.397428, 39.90923], // 默认中心点(学校坐标)
vehicles: []
}
},
methods: {
async initMap() {
const res = await this.$http.get('/api/vehicles')
this.vehicles = res.data
},
showVehicleInfo(vehicle) {
this.$router.push(`/vehicle/${vehicle.id}`)
}
}
}
</script>
4.2 状态管理
使用Vuex管理全局状态:
javascript复制// store/modules/user.js
const state = {
token: localStorage.getItem('token') || '',
userInfo: null
}
const mutations = {
SET_TOKEN(state, token) {
state.token = token
localStorage.setItem('token', token)
},
SET_USER_INFO(state, info) {
state.userInfo = info
}
}
const actions = {
async login({ commit }, { username, password }) {
const res = await axios.post('/api/login', { username, password })
commit('SET_TOKEN', res.data.access_token)
return res
}
}
5. 开发环境配置
5.1 PyCharm项目设置
- 创建Pure Python项目
- 配置Python解释器(建议使用3.7+)
- 安装依赖:
code复制pip install flask flask-sqlalchemy flask-migrate flask-jwt-extended requests - 配置运行配置:
- Script path:
app.py - Environment variables:
code复制FLASK_APP=app.py FLASK_ENV=development
- Script path:
5.2 数据库迁移
使用Flask-Migrate管理数据库变更:
bash复制flask db init # 初始化迁移目录
flask db migrate -m "initial migration" # 生成迁移脚本
flask db upgrade # 应用迁移
6. 部署方案
6.1 生产环境部署
采用Nginx + Gunicorn方案:
-
安装Gunicorn:
bash复制
pip install gunicorn -
启动服务:
bash复制
gunicorn -w 4 -b 127.0.0.1:8000 app:app -
Nginx配置:
nginx复制server { listen 80; server_name yourdomain.com; location / { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } location /static/ { alias /path/to/static/files; } }
6.2 监控与日志
使用Supervisor管理进程:
ini复制[program:campus_bike]
command=/path/to/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 app:app
directory=/path/to/project
user=www-data
autostart=true
autorestart=true
stderr_logfile=/var/log/campus_bike.err.log
stdout_logfile=/var/log/campus_bike.out.log
7. 项目优化与扩展
7.1 性能优化
-
数据库查询优化:
- 添加适当索引
- 使用SQLAlchemy的lazy loading
- 实现缓存层(Redis)
-
API响应优化:
- 启用Gzip压缩
- 实现分页查询
- 使用ETag缓存
7.2 功能扩展路线
-
智能调度系统:
- 基于历史数据预测车辆需求
- 自动调度车辆平衡各区域数量
-
电池管理系统:
- 实时监控电池健康状态
- 智能充电调度
-
信用体系:
- 建立用户信用评分
- 信用良好的用户享受优惠
8. 开发经验分享
在实际开发过程中,我们积累了一些有价值的经验:
-
跨域问题处理:
Flask后端需要配置CORS:python复制from flask_cors import CORS CORS(app, resources={r"/api/*": {"origins": "*"}}) -
JWT令牌刷新:
实现无感刷新机制提升用户体验:javascript复制// axios响应拦截器 axios.interceptors.response.use(response => { return response }, error => { if (error.response.status === 401) { return refreshToken().then(() => { return axios(error.config) }) } return Promise.reject(error) }) -
车辆状态同步:
使用WebSocket实现实时状态更新:python复制from flask_socketio import SocketIO socketio = SocketIO(app) @socketio.on('vehicle_status') def handle_vehicle_status(data): emit('status_update', {'vehicle_id': data['id'], 'status': 'updated'}, broadcast=True) -
测试策略:
- 单元测试:使用pytest测试核心逻辑
- API测试:使用Postman自动化测试
- E2E测试:使用Cypress测试完整流程
这个项目从技术选型到最终部署历时3个月,期间我们遇到了许多挑战,但也获得了宝贵的全栈开发经验。特别是Flask+Vue的组合,既保持了后端的灵活性,又提供了现代化的前端体验,非常适合校园场景的中小型项目开发。
