这个房屋租赁系统是一个基于Flask后端和Vue.js前端的全栈Web应用,旨在为房东和租客提供一个便捷的在线租赁平台。系统采用前后端分离架构,后端使用Python的Flask框架构建RESTful API,前端使用Vue.js实现响应式用户界面,数据库选用MySQL进行数据存储。
作为一名有多年全栈开发经验的工程师,我在实际项目中发现,房屋租赁系统有几个关键痛点需要解决:房源信息的实时更新、租赁合同的电子化管理、支付流程的安全可靠。这个项目正是针对这些需求设计的,适合有一定Python和JavaScript基础的开发者学习参考。
选择Flask作为后端框架主要基于以下考虑:
核心依赖库:
Vue.js作为前端框架的优势:
主要技术组合:
PyCharm专业版是开发Python后端的理想选择,它提供:
对于前端开发,VS Code配合Vue插件也是不错的选择:
采用JWT(JSON Web Token)实现无状态认证,流程如下:
关键实现细节:
python复制# JWT配置示例
app.config['SECRET_KEY'] = 'your-secret-key'
app.config['JWT_EXPIRATION_DELTA'] = timedelta(days=7)
# 登录接口
@app.route('/api/login', methods=['POST'])
def login():
data = request.get_json()
user = User.query.filter_by(email=data['email']).first()
if user and user.verify_password(data['password']):
token = jwt.encode({
'sub': user.id,
'iat': datetime.utcnow(),
'exp': datetime.utcnow() + app.config['JWT_EXPIRATION_DELTA']
}, app.config['SECRET_KEY'])
return jsonify({'token': token.decode('UTF-8')})
return jsonify({'error': 'Invalid credentials'}), 401
房源信息包括:
后端API设计:
code复制GET /api/houses - 获取房源列表
POST /api/houses - 创建新房源
GET /api/houses/<id> - 获取单个房源详情
PUT /api/houses/<id> - 更新房源信息
DELETE /api/houses/<id> - 删除房源
订单流程:
支付接口集成要点:
python复制# 支付宝支付示例
@app.route('/api/payment/alipay', methods=['POST'])
@jwt_required()
def create_alipay_payment():
order = create_order() # 创建订单
alipay = AliPay(
appid="your-app-id",
app_notify_url="http://example.com/notify",
app_private_key_string=app_private_key,
alipay_public_key_string=alipay_public_key,
sign_type="RSA2",
debug=True
)
order_string = alipay.api_alipay_trade_page_pay(
out_trade_no=order.order_no,
total_amount=str(order.amount),
subject=order.subject,
return_url="http://example.com/return",
notify_url="http://example.com/notify"
)
pay_url = "https://openapi.alipaydev.com/gateway.do?" + order_string
return jsonify({'pay_url': pay_url})
sql复制CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL,
email VARCHAR(100) NOT NULL UNIQUE,
password_hash VARCHAR(128) NOT NULL,
phone VARCHAR(20),
avatar VARCHAR(255),
is_landlord BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE houses (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(100) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
address VARCHAR(255) NOT NULL,
latitude DECIMAL(10,7),
longitude DECIMAL(10,7),
area INT,
room_type VARCHAR(50),
owner_id INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (owner_id) REFERENCES users(id)
);
CREATE TABLE orders (
id INT AUTO_INCREMENT PRIMARY KEY,
house_id INT NOT NULL,
tenant_id INT NOT NULL,
start_date DATE NOT NULL,
end_date DATE NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status ENUM('pending', 'paid', 'canceled', 'completed') DEFAULT 'pending',
contract_url VARCHAR(255),
payment_id VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (house_id) REFERENCES houses(id),
FOREIGN KEY (tenant_id) REFERENCES users(id)
);
code复制src/
├── components/
│ ├── HouseList.vue # 房源列表
│ ├── HouseDetail.vue # 房源详情
│ ├── UserProfile.vue # 用户资料
│ ├── OrderForm.vue # 订单表单
│ └── Payment.vue # 支付页面
├── views/
│ ├── Home.vue # 首页
│ ├── Login.vue # 登录页
│ └── Register.vue # 注册页
├── store/ # Vuex状态管理
└── router/ # 路由配置
javascript复制// HouseList.vue
<template>
<div class="house-list">
<el-card v-for="house in houses" :key="house.id" class="house-card">
<img :src="house.cover_image" class="house-image">
<div class="house-info">
<h3>{{ house.title }}</h3>
<p>{{ house.address }}</p>
<div class="price">{{ house.price }}元/月</div>
<el-button type="primary" @click="viewDetail(house.id)">查看详情</el-button>
</div>
</el-card>
<el-pagination
@current-change="handlePageChange"
:current-page="currentPage"
:page-size="pageSize"
:total="total"
layout="prev, pager, next">
</el-pagination>
</div>
</template>
<script>
export default {
data() {
return {
houses: [],
currentPage: 1,
pageSize: 10,
total: 0
}
},
mounted() {
this.fetchHouses()
},
methods: {
fetchHouses() {
axios.get('/api/houses', {
params: {
page: this.currentPage,
size: this.pageSize
}
}).then(response => {
this.houses = response.data.items
this.total = response.data.total
})
},
handlePageChange(page) {
this.currentPage = page
this.fetchHouses()
},
viewDetail(id) {
this.$router.push(`/houses/${id}`)
}
}
}
</script>
示例测试用例:
python复制# test_user.py
def test_user_registration(client):
response = client.post('/api/register', json={
'username': 'testuser',
'email': 'test@example.com',
'password': 'Test1234'
})
assert response.status_code == 201
assert 'id' in response.json
def test_user_login(client, test_user):
response = client.post('/api/login', json={
'email': test_user.email,
'password': 'password'
})
assert response.status_code == 200
assert 'token' in response.json
后端部署方案:
部署步骤:
bash复制# 安装依赖
pip install gunicorn
# 启动Gunicorn
gunicorn -w 4 -b 127.0.0.1:8000 app:app
# 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;
}
}
前端部署方案:
构建命令:
bash复制npm run build
在实际开发过程中,我总结了以下几点经验:
一个特别实用的技巧是在开发阶段启用Flask的调试模式,并配置前端代理,可以避免跨域问题:
javascript复制// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:5000',
changeOrigin: true
}
}
}
}
对于刚接触全栈开发的同行,我的建议是从小模块开始,逐步构建完整系统。比如先实现用户认证模块,确保前后端能正常通信,再逐步添加其他功能。遇到问题时,善用官方文档和开发者社区资源,大多数常见问题都能找到解决方案。