拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Flask搭配Django开发化妆预约系统:微信小程序全栈实践

Flask搭配Django开发化妆预约系统:微信小程序全栈实践 做化妆造预约系统前后端技术栈怎么配才顺手这个标题里同时出现了 Flask 和 Django老实说第一次看到的时候我也愣了一下——这两个框架平时很少出现在同一个项目里。但实际做下来你会发现这个组合不但不冲突反而把两边各自的强项发挥得挺到位Django 自带一套成熟的后台管理体系用来做运营管理端非常省事Flask 轻量灵活拿来写小程序要调的 API 接口代码结构清爽响应速度也容易控制。再加上微信小程序作为用户端入口整个系统基本覆盖了用户在小程序里下单、管理员在后台上架项目和处理订单这条完整链路。这套方案非常适合两类人参考一类是正在做毕业设计、需要快速落地一个全栈项目的同学另一类是美业门店想低成本搭建自己的预约系统、又不想被第三方 SaaS 平台抽成的经营者。下面我会从项目拆解、数据库设计、后端接口、小程序端开发到部署上线把我的做法和踩过的坑都整理出来。1. 项目整体设计与技术选型思路1.1 为什么是 Python 而不是 Java 或 Node化妆造服务预约系统本质上是一个信息管理 交易撮合的业务系统核心动作就是用户浏览服务、选时间、提交预约管理员接收订单、安排化妆师、处理状态。这类系统最大的特点就是 CRUD 密集、业务规则集中没有特别重的高并发要求。选 Python 就是看中两点一是开发效率高模型定义完直接跑迁移几分钟就能把数据库表建起来二是生态里 Flask 和 Django 两个框架正好对应轻接口和重后台两种需求不用硬着头皮在一个框架里塞所有东西。我当时的判断标准很简单如果只用 Flask后台管理界面得自己写一大堆 HTML 和 CRUD 视图浪费不少时间如果只用 Django做小程序接口又显得有点重——Django 的中间件、CSRF、Admin 这些机制对纯 JSON API 来说大部分用不上还要额外处理跨域。两个框架配合使用Flask 只负责 APIDjango 只负责后台职责边界非常清楚。1.2 Flask 和 Django 的职责划分这个项目里 Flask 和 Django 跑在同一个 Python 环境、连同一个数据库但各自只做自己擅长的事Flask 端只暴露微信小程序需要的 JSON 接口比如获取服务项目列表、获取化妆师排班、提交预约、查询订单状态。接口路径都在/api前缀下数据格式统一是 JSON不渲染任何 HTML 页面。Django 端作为管理后台处理管理员登录、服务项目增删改、化妆师信息维护、预约订单审核、排班管理这些操作。Django Admin 自带的用户权限模型只需要简单配置就能用改完数据直接写入同一张表Flask 接口立刻就能读到最新数据。这种一个业务系统、两个框架各管一头的做法听起来有点非主流但在中小型项目里非常实用。数据库表是两边共用的所以表结构的设计必须提前想清楚不能像单框架项目那样边写边改。我建议在动手写代码之前先把所有表结构定下来两边再各自开发避免后期字段对不上。1.3 为什么前端选微信小程序而不是 App 或 H5美业预约的使用场景基本都在微信里用户看到朋友圈或者公众号推荐点开小程序就能预约不用下载 App也不用跳转浏览器。微信小程序对创业者来说还有一个好处——不需要上架各大应用商店审核通过后直接通过微信搜索触达用户获客成本比原生 App 低一个量级。还有一个很现实的原因微信小程序的支付体系成熟用户习惯已经培养好了。虽然我这个版本先做的是预约后到店支付的简化流程但接口设计上已经预留了支付回调的扩展位后续接微信支付不用推翻重来。2. 核心功能模块与数据库设计2.1 功能模块拆解整个系统按角色分三类普通用户、化妆师、管理员。用户端面对的是小程序化妆师和管理员面对的是 Django 后台。用户端功能微信登录小程序端调用wx.login()获取 code后端通过 code 换取 openid作为用户唯一标识。浏览服务查看化妆造服务项目列表包括服务名称、图片、价格、时长、适用场景说明。选择化妆师每个服务项目下可以关联多个化妆师用户能看到化妆师的简介、评分、档期。提交预约选择日期和时段填写联系方式和备注提交后生成预约订单。订单管理查看自己的预约记录包括待确认、已确认、已完成、已取消四种状态支持取消预约。后台端功能服务项目管理上架、下架、编辑化妆造服务设置价格和时长。化妆师管理添加化妆师资料设置服务时段查看每位化妆师当天的预约情况。订单管理查看所有预约订单确认或拒绝预约标记订单完成。数据统计按日期统计预约量、营收虽然这版只做了简单聚合但 Django 后台的 ORM 写统计查询非常顺手。2.2 数据库表结构设计先说明一个原则所有表的关联字段在一开始就要设计好不要在开发过程中频繁改表结构。因为 Flask 和 Django 同时读写这些表改一次表结构要同步修改两边的模型和迁移文件非常容易出问题。我设计的核心表有六张用户表 user字段类型说明idint主键openidvarchar(64)微信 openid唯一nicknamevarchar(64)用户昵称avatar_urlvarchar(255)头像地址phonevarchar(20)手机号created_atdatetime注册时间服务项目表 service字段类型说明idint主键namevarchar(100)服务名称descriptiontext服务详情cover_imagevarchar(255)封面图pricedecimal(10,2)价格durationint服务时长分钟statustinyint1上架 0下架化妆师表 beautician字段类型说明idint主键namevarchar(50)姓名avatarvarchar(255)头像titlevarchar(100)头衔/职称introtext个人简介ratingdecimal(3,1)评分预约表 appointment字段类型说明idint主键user_idint用户 IDservice_idint服务 IDbeautician_idint化妆师 IDappoint_datedate预约日期start_timetime开始时间end_timetime结束时间remarkvarchar(255)用户备注statustinyint0待确认 1已确认 2已完成 3已取消created_atdatetime提交时间时段表 time_slot用于化妆师排班管理字段类型说明idint主键beautician_idint化妆师 IDwork_datedate排班日期slot_starttime时段开始slot_endtime时段结束is_bookedtinyint0空闲 1已预约评价表 review扩展用这版已实现基础字段表结构设计完接下来就要回答一个关键问题Flask 和 Django 怎么共用这六张表2.3 双框架共用数据库的模型同步方案这一步是这个项目最核心的工程难点。两个框架如果各自管理自己的一套模型表结构很容易出现不一致。我的做法是以 Django 的模型为主统一用 Django 的迁移工具建表Flask 端只定义和自己业务相关的映射模型不执行建表操作。具体操作是先在 Django 项目里建好所有 app 和 model执行python manage.py makemigrations和python manage.py migrate把表全部建出来。在 Flask 项目里用 SQLAlchemy 的定义db.Table或者db.Model映射到同一批表只声明字段严禁db.create_all()。因为create_all()默认只会建不存在的表如果两边字段定义不一致它不会帮你改只会默默地在表结构不同的情况下运行埋下隐患。Flask 端读取的表结构以实际数据库为准字段多出或缺失都报错后立刻查 Django 的模型定义保证两边对字段名、类型、长度完全一致。用这个方法我最开始遇到的Flask 写进去了记录Django 后台却查不到问题就再也没出现过。3. 后端接口开发与核心业务逻辑3.1 Flask 接口目录结构和统一返回格式Flask 端不是一个完整的 Web 应用它只做 API Server。我的项目结构是这样flask_api/ ├── app.py # 入口文件注册蓝图 ├── config.py # 数据库配置、微信小程序配置 ├── models.py # SQLAlchemy 模型映射 ├── utils.py # 工具函数token 校验、时间处理 ├── api/ │ ├── auth.py # 微信登录、token 签发 │ ├── services.py # 服务项目接口 │ ├── beautician.py # 化妆师接口 │ └── appointment.py # 预约相关接口 └── requirements.txtFlask 接口的返回格式我统一成下面这种结构小程序端解析起来特别方便不用每个接口单独判断字段{ code: 0, message: success, data: {} }code为 0 表示成功非 0 表示业务错误比如 40001 表示该时段已被预约40002 表示服务已下架。小程序端只需判断code都不用看message就能决定下一步动作。3.2 微信登录与 Token 校验微信小程序登录的流程核心是前端拿code换后端返回的openid然后后端自己签发一个 token 给小程序后续所有请求都带这个 token。Flask 端通过requests调用微信官方接口换取 openid这一步有个容易犯的错直接拿官方接口返回的session_key当登录态。session_key是微信用来解密用户手机号等敏感信息的不能暴露给前端。正确做法是后端自己生成 token我这里用的是itsdangerous生成带时间戳的签名 token有效期设为 7 天用户重新打开小程序时如果 token 过期就重新静默登录。核心代码# auth.py from itsdangerous import TimedJSONWebSignatureSerializer as Serializer def generate_token(openid): s Serializer(app.config[SECRET_KEY], expires_in7 * 86400) return s.dumps({openid: openid}).decode() def verify_token(token): s Serializer(app.config[SECRET_KEY]) try: data s.loads(token) return data[openid] except Exception: return None这个方案的优点是服务端无状态不需要把 token 存数据库Flask 和 Django 之间也不需要共享会话。小程序端的每个请求都在header里带上Authorization: Bearer tokenFlask 写一个before_request装饰器统一校验除了登录接口之外全部放行。3.3 预约冲突检测——这里最需要花功夫如果你只是简单地让用户提交预约就 INSERT 一条记录上线之后很快会发现一个问题同一个化妆师在同一个时间段被预约了两次。预约系统最核心的并发控制就是这里。我的解决思路是两重保险第一重数据库层面。给 appointment 表加一个唯一约束字段组合是(beautician_id, appoint_date, start_time)这样即便代码层面漏了判断数据库也会拒绝重复插入。第二重应用层面。查询待确认和已确认状态下的预约记录判断新提交的时间段是否交叉。这里的判断逻辑不能只查绝对相等的时间因为服务时长不同——化妆服务 60 分钟美发服务可能 90 分钟用户约了 10:00 的化妆下一个用户不能约 10:30 的美发因为时间重叠了。判断代码def check_time_conflict(beautician_id, date, start_time, end_time): conflict Appointment.query.filter( Appointment.beautician_id beautician_id, Appointment.appoint_date date, Appointment.status.in_([0, 1]), # 待确认和已确认都算占用 Appointment.start_time end_time, Appointment.end_time start_time ).first() return conflict is not None这段 SQL 的语义是两条预约时间段是否存在交集。一开始我用的是start_time conflict_end_time这种写法结果时段相邻的预约被误判为冲突后来改成上面的严格小于判断前后两个时段恰好首尾相接时就不会误伤。还有事务问题提交预约时先查冲突再 INSERT这两个操作要放在同一个db.session事务里避免两个请求同时查到不冲突然后一起插入。遇到数据库唯一约束冲突时捕获异常返回该时段已被预约用户刷新后自然能看到新状态。3.4 Django 后台的模型与 Admin 配置Django 端的事情就纯粹多了核心工作是把六张表转换成 Django 的 model然后注册进 Admin 后台。有一个容易踩坑的点不要在 Django 里用AutoField之外的字段类型和 Flask 端映射错位。比如数据库里status字段是tinyintDjango 模型里就建议也用SmallIntegerField不要图省事用BooleanField因为预约状态有 4 种取值BooleanField装不下。同理price字段用DecimalField(max_digits10, decimal_places2)两边保持一致。Admin 注册时我做了几件事订单列表按创建时间倒序默认展示最近 20 条避免数据一多页面卡顿。服务项目的status字段用list_editable直接支持列表页切换上架/下架。预约订单添加自定义筛选按日期筛选、按状态筛选、按化妆师筛选。化妆师详情页内嵌预约记录用TabularInline显示当天所有预约方便查档期。Django Admin 有一个你可能没注意的技巧定义get_queryset时用select_related把关联的外键字段提前 join 出来列表页加载速度会有肉眼可见的提升尤其是订单多了以后。4. 小程序端开发实战与关键细节4.1 小程序项目的基本结构前端我选的是原生微信小程序没用 uniapp。原因很简单这个业务只有 6 个页面原生开发完全够用而且原生小程序在调试工具里出现问题更好排查。uniapp 确实能一套代码多端复用但如果你没有同时要出 H5 和 App 的需求引入框架反而增加心智负担。页面规划如下pages/index/index首页展示服务项目轮播图和列表pages/service/detail服务详情页选择化妆师和日期时段pages/appointment/confirm预约确认页填写联系方式和备注pages/order/list订单列表页按状态切换查看pages/order/detail订单详情页展示预约信息和状态操作pages/profile/profile个人中心显示用户信息和登录状态4.2 小程序调用后端接口的两种方式对比小程序发请求官方推荐用wx.request这里有一个开发环境适配要提前处理微信开发者工具里默认不校验合法域名但真机上必须配置 HTTPS 域名且域名需要在小程序后台添加白名单。开发阶段我的做法是在app.js里定义一个全局变量baseUrl开发环境填http://127.0.0.1:5000生产环境填线上 HTTPS 域名。因为本地开发用的是 HTTP开发者工具里要勾选不校验合法域名选项否则请求会被拦截。后端 Flask 还要配置 CORS 跨域。这里提个醒flask-cors的默认配置是全放行开发阶段方便但上线前一定把origins参数改成白名单避免任何网站都能调你的接口。wx.request封装好公共请求函数后小程序端不用关心 token 的获取逻辑统一在请求头里带上。我的封装// utils/request.js const request (url, method, data) { const token wx.getStorageSync(token); return new Promise((resolve, reject) { wx.request({ url: getApp().globalData.baseUrl url, method, data, header: { Content-Type: application/json, Authorization: Bearer token }, success: (res) { if (res.data.code 0) { resolve(res.data.data); } else { wx.showToast({ title: res.data.message, icon: none }); reject(res.data); } }, fail: (err) reject(err) }); }); };4.3 预约选择页面的时段加载逻辑用户在服务详情页选择化妆师之后需要展示这位化妆师在选定日期下可预约的时段。这个逻辑前端不能自己算必须交给后端原因很简单后端才知道每个化妆师今天有没有排班、已经预约了多少单。小程序端发请求时会带beautician_id和date两个参数Flask 查询出当天这个化妆师的排班数据再过滤掉已经被预约的时段返回可预约列表。前端拿到列表渲染成选择项。这里有一个体验层面的细节时间段按钮建议用禁用态而不是隐藏。用户看到灰色的不可选时段会理解这是已被约走的如果直接不显示用户会觉得页面加载不全甚至会反复刷新怀疑出 bug 了。4.4 缓存与登录状态的正确处理微信小程序的缓存我用得很克制只存了三类数据token登录凭证7 天有效。用户昵称和头像用于个人中心展示避免每次打开都重新调接口。服务项目列表的简单缓存设置 30 分钟失效小程序端获取时先读缓存过期再请求。有一个细节提醒大家wx.getStorageSync读取缓存时如果 key 不存在返回的是空字符串而不是null。判断缓存是否存在时不要用if (cache null)要用if (cache || cache null)否则会出现第一次进页面时缓存穿透的问题。关于顶部导航栏不同机型状态栏高度不一样iPhone 有刘海屏安卓机型状态栏高度各不相同。写死状态栏高度会在部分机型上出现布局错位。正确做法是通过wx.getSystemInfoSync()获取statusBarHeight动态计算导航栏每部分的高度。这个坑我第一次做小程序时踩过后来写的所有小程序都统一封装了这个适配方法。4.5 表单校验与提交预约预约确认页面用户要填手机号和备注表单校验看似简单但有几个边角情况手机号校验我用的是^1[3-9]\d{9}$这个正则覆盖了目前所有正常手机号段。但注意有些用户会填座机号如果严格按手机号校验会拦截所以在设计上要区分预约联系和账号绑定两种场景。我的处理是预约时的手机号允许留空填了则校验格式留空后管理员可以在后台看到用户通过微信客服联系的入口不做强制。提交预约成功后不要立刻wx.navigateBack返回上一页而是弹出一个成功提示然后跳转到订单详情页。用户看到预约成功这个明确反馈比返回到列表页自己找订单要好得多。订单详情页再展示商家确认中请留意微信通知的说明。5. 部署、联调与常见问题排查5.1 本地部署环境和依赖管理项目的 Python 环境我推荐用虚拟环境不要直接装全局。两个框架依赖可以装同一个虚拟环境里因为 Flask 和 Django 在依赖层面本身没有冲突。我的requirements.txt里核心依赖就这些flask2.3.3 flask-sqlalchemy3.0.5 flask-cors4.0.0 requests2.31.0 itsdangerous2.1.2 django4.2.7 mysqlclient2.2.0数据库我用的 MySQL 5.7因为微信小程序云开发虽然免费但没法让 Flask 和 Django 直接连接还是自建 MySQL 最可控。数据库编码一定要设成utf8mb4因为用户昵称会包含 emoji 和生僻字utf8会报错。启动方式分两个终端# Flask cd flask_api source venv/bin/activate python app.py # Django cd django_admin python manage.py runserver 0.0.0.0:8000注意两个服务占用不同端口Flask 跑 5000Django 跑 8000这样同时开发互不干扰。5.2 线上部署的注意事项线上部署我没用特别复杂的架构一台云服务器就搞定了。几个关键点Flask 不要用自带的开发服务器上线app.run()那个是单线程的并发一高就卡。我用了waitress纯 Python 实现的 WSGI 服务器安装简单一行命令启动waitress-serve --port5000 app:app。Django 用gunicorn或者uwsgi都行我用的是gunicorn配合 Nginx 反向代理。Nginx 配置两个 server 块一个把api.你的域名.com代理到 Flask 的 5000 端口一个把admin.你的域名.com代理到 Django 的 8000 端口。小程序要求所有请求必须是 HTTPS所以服务器上必须配置 SSL 证书。我用的是免费证书配置 Nginx 时把 HTTP 流量强制跳转到 HTTPS避免小程序真机上请求失败。数据库备份也很重要我写了一个每天凌晨 3 点自动备份的 cron 任务备份文件保留 7 天。预约数据是门店的核心资产丢了很难找回。5.3 高频问题排查实录问题一Django 后台修改了数据Flask 接口查不到先查数据库确认数据已经写入如果写入成功但没有查到绝大多数是缓存问题。我用 SQLAlchemy 时默认没有开查询缓存但如果用了query的.all()结果又被手动缓存过就会出这种事。排查方法很简单重启 Flask 进程再请求一次如果恢复了就是进程内缓存在代码里去掉缓存逻辑就好。问题二小程序请求接口一直提示开发者工具未配置合法域名这个看报错信息就能定位。开发阶段就在详情页勾选不校验合法域名上线之前一定记得在小程序后台配置 request 合法域名。有个坑是配置完域名后要等几分钟才生效而且开发者工具里要重新编译一次才加载新配置。问题三提交预约后接口返回 500日志显示数据库事务超时这个我遇到过原因是预约冲突检测和插入操作之间发生了死锁。两个用户同时预约同一个化妆师同一时段时两个事务都先查后插互相持有锁等待对方释放。解决方法是在 Flask 中把冲突检测和插入放到同一个事务里并设置数据库隔离级别为READ COMMITTED同时捕获唯一约束冲突异常。加一个try-except把 500 变成业务错误提示用户体验完全不同。问题四Django 里删除对象时关联数据报错Django 的 ForeignKey 默认是PROTECT行为删除有子记录的父对象时会被拦截提示无法删除。如果你确实需要级联删除要在模型定义时指定on_deletemodels.CASCADE。但预约记录这种数据我建议用软删除而不是物理删除加一个is_deleted字段删除操作只更新字段不真的删记录。这样以后要查历史订单、做数据统计数据都还在。问题五时间显示的时区问题如果你配置了TIME_ZONE UTC存进数据库的时间会比北京时间少 8 小时。预约场景里时间错了是灾难性的。统一方案是Django 设置TIME_ZONE Asia/Shanghai和USE_TZ TrueFlask 端所有时间读写也统一使用本地时间不要用 UTC。前后端约定所有时间参数只传日期和HH:MM:SS格式的字符串不传时间戳避免时区转换出错。5.4 关于数据安全和接口鉴权最后聊一下接口安全。很多人做小程序后端时觉得反正只有自己的小程序在调接口就不做鉴权了这是个巨大的坑。任何知道你的接口地址的人都可以直接构造请求调用你的预约接口批量下单把你的化妆师档期占满。基础防护我做了四层微信登录返回的 token 必须校验没有合法 token 的请求直接返回 401。Flask 端写了一个before_request钩子白名单放行/api/auth/login其余接口全部校验 token。对提交预约、取消预约这类写操作校验请求频率同一用户每分钟最多 20 次超出直接拒绝。用简单的进程内计数器实现不引额外组件。手机端提交的数据全部做后端校验绝对不信任前端传过来的价格、时长、化妆师 ID。服务项目信息和价格必须是后端根据service_id自己查出来的前端传什么价格字段都忽略。我见过只验证前端传值的结果用户把价格改成 0 元下单的案例后端校验一下就能彻底堵住。还有一个容易被忽略的点小程序端展示的图片如果用的是自己的服务器存储要注意图片访问路径不能泄露服务器目录结构。我这边处理方式是通过 Nginx 的 alias 映射把实际目录遮蔽掉图片 URL 统一用/media/xxx.jpg这样的格式对外。一些个人体会这套 Flask Django 微信小程序组合整体做下来最大的感受就是边界清晰。后端两个框架各管各的你不用在 Flask 里硬写管理后台也不用在 Django 里为了返回 JSON 绕过一堆默认机制省下来的时间正好都花在业务的打磨上——比如预约冲突检测做得更严谨、小程序端交互做得更顺手。如果你也正在规划一个类似的预约系统我建议你从数据库表设计开始先把所有表的字段、关联关系、状态枚举定义清楚再动手写代码。双框架项目最怕中途改表结构改一处两边都要跟着改工作量翻倍。最后送你一个我在实际项目里常用的自检清单接口返回格式是否统一、事务边界是否清晰、预约冲突判断是否覆盖了所有状态、线上环境是否已经开启 HTTPS、小程序请求域名是否已配置。把这几条跑一遍你的系统基本可以稳定跑起来。我后来把这个项目的经验复制到了美容、美发和摄影棚预约好几个场景核心代码改一改字段就能复用这套底子是靠谱的。
返回列表