
简介这是一份基于Flask与MySQL的租房后台系统完整源码包适用于具备基础Python知识、希望学习Web开发或快速搭建租房管理平台的开发者。项目实现了房源管理、订单处理及支付宝支付等核心业务配合部署文档和SQL数据文件可帮助读者理解Flask项目结构、数据库设计与第三方支付集成方式。压缩包共37个文件涵盖Python代码、部署说明、数据库脚本、配置文件及界面截图等其中19个py文件用于实现业务逻辑与API接口3个md文档提供部署与使用指引整体体积仅478KB轻量清晰。已有104人学习下载。借助这份资料读者可掌握Flask与MySQL的整合思路并参照文档从零部署运行也可基于现有模块进行二次开发作为毕业设计或课程项目同样适用是学习Python后端项目的实用参考。1. 这套 Flask 租房后台值得你拆一遍的不只是支付做后台管理系统的人大多有个共识CRUD 写得再熟遇到「真实业务 第三方支付」的组合依然会卡在流程设计上。这套基于 Flask MySQL 的租房后台系统正好把两端都占了——房源、订单、租客、后台管理的完整业务闭环加一个能跑通的支付宝电脑网站支付。我在本地把整套源码跑起来看过目录结构也在支付沙箱里模拟过下单和回调结论是它更适合拿来当「完整业务脚手架」用而不是只当一个毕业设计。源码里有ihome业务包、web.py入口、api_v1接口层、models.py数据模型还有一份带测试数据的ihome.sql和两份部署文档。Python 3.7 以上即可运行依赖记录在requirement.txt里。本文不会逐行翻译源码而是按「数据模型 → 支付流程 → 部署上线 → 排错验证」这条主线把值得抄的代码和容易踩的坑一次说清。2. Flask 蓝图 MySQL 数据模型后台系统先立骨架2.1 蓝图划分决定了你能接多大业务打开ihome包能看到典型的 Flask 应用工厂模式web.py负责创建 app、注册蓝图、初始化配置api_v1目录下按业务域拆分接口模块。拆分的思路值得学习——不是按「前台/后台」拆而是按「资源 动作」拆。常见做法是# web.py — Flask 应用工厂与蓝图注册 from flask import Flask from ihome.api_v1 import api def create_app(config): app Flask(__name__) app.config.from_object(config) # 注册蓝图url_prefix 一次定好避免后续接口路径混乱 app.register_blueprint(api, url_prefix/api/v1) # 启动时建表配合 ihome.sql 中的导出结构 from ihome.models import db db.init_app(app) with app.app_context(): db.create_all() return app这段代码要说明三点。第一config.py里集中管理数据库 URI、密钥、支付宝参数不要散落在各个视图函数里第二url_prefix用/api/v1做版本控制后续接口升级不用破坏旧调用方第三create_all()适合开发环境生产环境应直接用ihome.sql导入避免模型与库表不同步。2.2 房源、订单、用户三张核心表怎么设计models.py里定义了 SQLAlchemy 模型。租房后台最核心的表是用户表、房源表、订单表。订单表的设计会直接影响支付功能的实现——支付宝回调回来时你需要通过订单号定位记录并更新状态所以订单号必须唯一且要带上业务前缀。# ihome/models.py — 核心数据模型精简 from datetime import datetime from ihome import db class Order(db.Model): __tablename__ ih_order id db.Column(db.Integer, primary_keyTrue, autoincrementTrue) order_no db.Column(db.String(64), uniqueTrue, nullableFalse, indexTrue, comment业务订单号) amount db.Column(db.Numeric(10, 2), nullableFalse, comment支付金额单位元) status db.Column(db.String(16), defaultWAIT_PAY, comment待支付/已支付/已取消/已退款) user_id db.Column(db.Integer, db.ForeignKey(ih_user.id), nullableFalse) house_id db.Column(db.Integer, db.ForeignKey(ih_house.id), nullableFalse) create_time db.Column(db.DateTime, defaultdatetime.now, comment创建时间) pay_time db.Column(db.DateTime, nullableTrue, comment支付成功时间)设计上有两个细节值得关注。金额字段用Numeric(10, 2)而不是 Float——支付场景下 Float 的二进制精度问题会导致对账差几分钱订单状态用字符串常量而不是数字枚举便于日志排查时直接读懂状态。order_no加了唯一约束和索引支付宝回调依赖这个字段做幂等任何情况下都不能重复。2.3 为什么不建议直接改 ihome.sql项目附带的ihome.sql是完整的建库脚本含测试数据。我用 Navicat 导入测试过ihome库名、表前缀ih_、字段注释都齐全。但我建议你把它当作「参考实现」自己项目里按需裁剪——比如去掉你不需要的字段或者把create_time改成DATETIME带默认值。-- ihome.sql 中的订单表节选注意 utf8mb4 字符集 CREATE TABLE ih_order ( id int(11) NOT NULL AUTO_INCREMENT, order_no varchar(64) NOT NULL COMMENT 订单编号, amount decimal(10,2) NOT NULL COMMENT 支付金额, status varchar(16) DEFAULT WAIT_PAY COMMENT 订单状态, PRIMARY KEY (id), UNIQUE KEY uk_order_no (order_no) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT订单表;这里要解释几个点。utf8mb4是必须的——房源描述里可能有 emoji 或生僻字utf8存不下订单金额必须用decimal避免应用层精度问题引擎选 InnoDB 是因为订单表有事务需求支付状态更新时要在事务里完成「改订单状态 写流水」两个动作。3. 支付宝支付接入从下单到异步回调的完整链路3.1 支付流程要先在脑子里画清楚支付宝电脑网站支付的标准流程是后端生成订单 → 拼接支付参数并签名 → 返回支付表单给前端自动提交 → 用户扫码付款 → 支付宝同步跳转return_url 异步通知notify_url→ 后端验签、改单。这套源码里封装了utils/const.py和支付相关模块把 appid、私钥、支付宝公钥、回调地址都集中在配置里。凡是做支付都要记住一个原则同步跳转不可信异步通知才可靠。用户在浏览器里关掉页面、断网、点返回同步跳转都可能丢但支付宝会反复发送异步通知直到你的服务器返回success。所以改订单状态、触发后续发货逻辑必须在notify_url对应的视图里完成。3.2 首发请求签名参数拼接的细节不能错# 支付接口核心逻辑 — 拼接请求参数并签名 from alipay import AliPay from ihome.utils.const import ALIPAY_APPID, ALIPAY_PRIVATE_KEY, ALIPAY_PUBLIC_KEY, ALIPAY_RETURN_URL, ALIPAY_NOTIFY_URL alipay AliPay( appidALIPAY_APPID, app_notify_urlALIPAY_NOTIFY_URL, app_private_key_stringALIPAY_PRIVATE_KEY, alipay_public_key_stringALIPAY_PUBLIC_KEY, sign_typeRSA2 ) def build_pay_params(order): # out_trade_no 必须与数据库订单号一致用于回调时定位 order_string alipay.api_alipay_trade_page_pay( out_trade_noorder.order_no, total_amountstr(order.amount), # 转字符串SDK 不接受浮点 subjectf租房订单-{order.house_id}, return_urlALIPAY_RETURN_URL, notify_urlALIPAY_NOTIFY_URL ) return fhttps://openapi.alipay.com/gateway.do?{order_string}这里有两个坑是新手高频踩的。第一total_amount必须是字符串SDK 内部做 URL 编码时会处理类型问题传 Float 会导致序列化异常第二out_trade_no一定要用数据库里那条订单的order_no不要在前端传否则任何人都可以改订单号替别人付款。return_url是 GET 跳转notify_url是 POST 通知两者职责完全不同。3.3 异步回调验签与订单状态流转# notify 回调视图 — 验签 幂等更新 app.route(/api/v1/order/pay/notify, methods[POST]) def order_notify(): data request.form.to_dict() signature data.pop(sign, None) # 第一步验签确保请求确实来自支付宝 success alipay.verify(data, signature) if not success: return failure # 第二步读取关键业务参数 out_trade_no data.get(out_trade_no) trade_status data.get(trade_status) trade_no data.get(trade_no) # 支付宝交易号 # 第三步只处理成功状态且做幂等判断 if trade_status TRADE_SUCCESS: order Order.query.filter_by(order_noout_trade_no).first() if order and order.status WAIT_PAY: order.status PAID order.pay_time datetime.now() order.trade_no trade_no # 保存第三方交易号方便对账 db.session.commit() return success return success # 已处理过的重复通知也要返回 success return success验签逻辑是支付安全的关键防线。alipay.verify(data, signature)内部用支付宝公钥对参数做 RSA2 验签防止伪造通知。拿到trade_status后只认TRADE_SUCCESSTRADE_FINISHED不用处理退款场景。幂等判断放在「状态从 WAIT_PAY 到 PAID」这个条件上数据库层面配合订单号唯一索引保证重复通知不会产生两条流水。3.4 同步跳转只做提示不做业务return_url对应的视图一般只做一件事——查一下订单当前状态然后渲染支付结果页。app.route(/api/v1/order/pay/return, methods[GET]) def pay_return(): order_no request.args.get(out_trade_no) order Order.query.filter_by(order_noorder_no).first() if order: return f订单 {order.order_no} 状态{order.status} return 订单不存在注意这里用了「订单状态」而不是「支付结果」作为返回内容的依据。因为同步通知到达时异步通知可能还没到订单还停在WAIT_PAY。用户看到「待支付」很正常前端要做的是轮询后端订单状态接口而不是直接信同步跳转的参数。4. 从 SQL 到服务上线部署文档没写透的六个步骤4.1 环境准备Python 版本和虚拟环境先锁定部署文档要求 Python 3.7实际运行建议 3.83.10。3.11 以上部分旧版依赖比如某些版本的Werkzeug会报兼容错误这个后面排错章节会专门说。# 创建虚拟环境避免污染系统 Python python3 -m venv venv source venv/bin/activate # 按 requirement.txt 安装依赖 pip install -r requirement.txt -i https://pypi.tuna.tsinghua.edu.cn/simple虚拟环境是这步的核心不要跳过。租房系统涉及 Flask、SQLAlchemy、PyMySQL、alipay-sdk-python 等依赖版本之间互相有约束直接装在系统 Python 里后期升级风险很大。安装源换成清华镜像国内网络环境下能省下大量等待时间。4.2 MySQL 建库与数据导入# 登录 MySQL创建数据库并导入项目 SQL mysql -uroot -p # 在 MySQL 命令行中执行 CREATE DATABASE ihome DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE ihome; SOURCE /path/to/ihome.sql;导入成功后检查表数量和测试数据SHOW TABLES; SELECT COUNT(*) FROM ih_order;如果SOURCE因为 SQL 文件过大而中断改用命令行重定向导入mysql -uroot -p ihome ihome.sql这一步最常出的问题是字符集。如果建库时用了默认的latin1房源描述里的中文会全部变成乱码。所以建库语句里的utf8mb4和utf8mb4_general_ci不要省。4.3 修改配置数据库密码和支付宝参数config.py中需要改三处——数据库连接串、支付宝 appid、密钥路径。连接串格式为mysqlpymysql://用户名:密码127.0.0.1:3306/ihome。# config.py 关键配置段 class Config: # 数据库连接密码含特殊字符需 URL 编码 SQLALCHEMY_DATABASE_URI mysqlpymysql://root:your_password127.0.0.1:3306/ihome?charsetutf8mb4 SQLALCHEMY_TRACK_MODIFICATIONS False # 支付宝沙箱 / 正式环境切换 ALIPAY_APPID 你的appid ALIPAY_PRIVATE_KEY open(app_private_key.pem).read() ALIPAY_PUBLIC_KEY open(alipay_public_key.pem).read() ALIPAY_RETURN_URL http://your_domain/api/v1/order/pay/return ALIPAY_NOTIFY_URL http://your_domain/api/v1/order/pay/notify密码里有或#时必须做 URL 编码比如密码是abc123写成abc%40123。支付宝要区分沙箱环境和正式环境沙箱时网关用openapi-sandbox.dl.alipaydev.com正式环境用openapi.alipay.comSDK 里通常有api_host参数可以切换。4.4 启动服务与接口自测python web.py # 或者用 flask 命令指定入口 FLASK_APPweb.py FLASK_ENVdevelopment flask run --host0.0.0.0 --port5000启动后先用 curl 验证接口是否通curl http://127.0.0.1:5000/api/v1/house/list返回 JSON 数据说明接口层正常。接着测试用户登录和房源详情的接口确认数据库连接没有问题。如果页面能打开但接口 500重点看 Flask 日志里是pymysql连接报错还是模型映射报错。4.5 内网穿透调试支付宝回调支付宝的notify_url必须是公网可以访问的地址本地开发时用内网穿透工具把 5000 端口暴露出去。常见做法是用natapp或cpolar./natapp authtoken你的token ./natapp -authtoken你的token -localport5000启动后会得到一个公网域名把这个域名填到支付宝开放平台的应用回调地址配置里同时改掉config.py中的ALIPAY_RETURN_URL和ALIPAY_NOTIFY_URL。内网穿透调试的核心是「回调地址必须和支付宝后台配置一致」差一个路径都会导致验签后失败。4.6 用 Gunicorn 承载线上服务开发环境python web.py跑的是自带的开发服务器只支持单进程并发一高就卡。线上部署常用 Gunicorn 启动gunicorn -w 4 -b 0.0.0.0:8000 web:app-w 4表示开 4 个 worker 进程web:app表示从web.py中导入app对象。注意create_app()返回的才是 app 实例如果入口文件写成app create_app(Config)启动命令就应该对应调整。多 worker 下要确保数据库连接池配置合理SQLAlchemy 的pool_size可以适当调大。5. 回调幂等与金额精度线上最容易翻车的两个细节5.1 支付宝回调的重复通知要能扛住支付宝的异步通知机制是「不成功不放手」——你的服务器返回failure或超时支付宝会按递增间隔重发 8 次。这意味着同一笔订单可能收到多次TRADE_SUCCESS通知。很多人第一次做支付在回调里直接order.status PAID然后commit重复通知到达时再次执行虽然结果一样但如果有「支付成功后加积分、发短信」这类副作用就会重复触发。正确的做法是加状态判断# 只有待支付状态才更新已支付直接返回成功 if order.status WAIT_PAY: order.status PAID db.session.commit() return success数据库层面再加一道保险——Order.query.filter_by(order_noorder_no, statusWAIT_PAY).update()配合受影响行数为 0 的判断彻底避免并发重复更新。5.2 金额单位与精度一分钱都不能差支付宝默认以元为单位金额精确到小数点后两位。源码里订单表用Numeric(10, 2)模型层传给支付宝时转成字符串。但有一种情况容易漏——退款或部分退款时数据库里的金额可能存在 0.1 0.2 0.30000000000000004 这类浮点误差。所有金额运算必须用Decimalfrom decimal import Decimal refund_amount Decimal(0.30) # 不要用 0.1 0.2 order.refund_amount (order.amount - Decimal(10.00)).quantize(Decimal(0.01))5.3 验签失败先看密钥格式调试阶段最常见的报错是验签失败或签名异常。九成原因是密钥格式问题。支付宝生成的私钥是 PKCS8 格式Python SDK 需要的是「去掉头和尾、去掉换行」的纯文本或者-----BEGIN RSA PRIVATE KEY-----标准的 PKCS1 格式。用 OpenSSL 转换# PKCS8 转 PKCS1 openssl rsa -in pkcs8_private_key.pem -out pkcs1_private_key.pem还有一种情况是公钥配错了——支付宝后台有「支付宝公钥」和「应用公钥」两个概念SDK 里传的是支付宝公钥支付宝自己生成的不是你自己上传的那把应用公钥。这两个搞混会出现签名能生成但验签永远失败的现象。5.4 订单查询接口与对账思路回调可能丢失所以还要提供一个主动查询订单状态的接口用于前端轮询和定时对账app.route(/api/v1/order/int:order_id/status, methods[GET]) def get_order_status(order_id): order Order.query.get(order_id) if not order: return jsonify({code: 404, msg: 订单不存在}) return jsonify({code: 0, data: {order_no: order.order_no, status: order.status}})定时对账脚本用 Flask 的before_request或单独 cron 任务把昨天所有WAIT_PAY状态的订单调支付宝查询接口核对真实支付状态差异数据单独落表。这套机制配合异步通知才能保证「用户付了钱但订单没更新」这种极端情况能被发现。我一般会把对账结果写到ih_order_check_log表字段包括订单号、支付宝交易号、本地状态、远端状态、检查时间出了问题可以直接查表定位。本文还有配套的精品资源点击获取