Python+Vue家政预约平台开发实战:从状态机到部署
2026/9/15 4:43:39 网站建设 项目流程

1. 家政预约平台的核心业务拆解与用户痛点

先说结论:家政预约平台这种项目,看起来就是个"下单-接单"的简单流程,实际做起来要处理的业务状态和角色权限比想象中复杂得多。我见过不少初学者一上来就写代码,结果做到一半发现表结构设计不合理、预约状态流转混乱,最后推倒重来。所以这篇就围绕"Python + Vue"这条技术主线,把家政预约平台从需求分析到落地实现的完整链路走一遍,顺便把我实际踩过的坑一并交代清楚。

先说清楚这个平台到底要解决什么问题。家政服务行业的预约场景有个特点:非标准化。同样是"保洁"服务,可能按小时计费,也可能按面积计费,还可能有"日常保洁""深度保洁""开荒保洁"之分。客户预约的不只是一个服务名称,而是"什么时间、什么服务类型、哪个家政人员、服务几小时、地址在哪、有没有特殊要求"这一整套信息。与此同时,家政人员端需要看到待接单、已接单、服务完成、订单评价这些状态;管理员端则需要处理人员审核、服务项目管理、订单抽成、投诉退款等情况。三方角色的诉求完全不同,这直接决定了系统的表结构和接口设计不能套用普通电商模板。

从功能地图上看,这个平台最少要拆成五个核心模块:

  • 用户端:注册登录、家政服务浏览、服务详情查看、在线预约下单、订单查询与取消、服务评价。
  • 家政人员端:身份认证入驻、技能标签维护、订单接单/拒单、服务状态更新(开始服务、完成服务)。
  • 管理后台:家政人员入驻审核、服务分类与定价管理、订单状态监控、用户反馈处理。
  • 消息通知:订单状态变化时,通过站内信或短信通知相关人员。
  • 数据统计:平台订单量、营收流水、热门服务品类等基础报表。

这五个模块中,"预约状态流转"是整个系统的核心脉络。我在设计时把订单状态定义成八个:待支付、已支付(待派单)、已派单(待服务)、服务中、待验收、已完成、已取消、退款中。每条预约记录都维护一个状态变更时间线,方便后续做纠纷追溯和统计。

实际开发中很多同学容易忽略的是:状态流转不是简单的枚举字段变更,它牵扯到一系列副作用操作。比如"待派单"变成"已派单"时,可能要锁定家政人员某个时间段的可约状态;"已完成"时,要触发佣金分成计算。这些副作用如果散落在控制器里到处写,后期维护会非常痛苦。我的做法是单独抽一个OrderStateMachine类集中管理流转逻辑,每个状态变更都走同一个入口,先校验合法性,再执行副作用,最后落库。

另外还有一个容易漏掉的点:预约冲突检测。同一个家政人员同一时段不能接两单,这不只是业务规则,更是数据一致性问题。在MySQL中,单纯靠应用层SELECT再INSERT很容易出现并发覆盖。我最后是在appointment_time_startappointment_time_end字段上设计了基于"人员ID + 时间窗口"的数据库唯一约束,并在事务里采用"悲观锁 + 冲突条件重新查询"的双保险策略。这个细节在前期需求分析时就要想清楚,否则后期补代价很大。

2. Django还是Flask:这个项目我为什么选了Django

标题里同时出现了Django和Flask,其实这两个框架在这个场景下都有成熟的应用案例。但"能用"和"适合"是两码事。我个人的选择非常明确:这种带完整后台管理的业务系统,优先选Django。原因拆开讲。

2.1 Django自带的Admin后台能省掉大量重复工作

家政预约平台里有一个非常现实的需求:运营人员需要维护服务类型、审核家政人员的入驻资料、查看订单流水、处理用户投诉。这些操作本质上就是增删改查。如果选Flask,所有这些后台页面都要用HTML模板或前端框架现写,工作量非常可观。Django自带的Admin后台虽然默认样式一般,但配合django-import-exportsimpleui这类第三方库,稍微改造一下就能达到"运营人员直接用"的程度,至少能覆盖原型阶段的全部后台需求。

以我做的家政平台为例,家政人员入驻审核这个场景,我在Admin后台里注册了Housekeeper模型,list_display展示姓名、服务技能、服务区域、审核状态,list_filter按审核状态筛选,actions添加"批量通过审核"的操作。整个审核后台大概只花了半天时间就搞定,这个效率优势在项目早期阶段非常关键。

2.2 ORM与模型层设计在业务复杂时更有优势

家政预约的数据库建模绕不开一个话题:服务规格。同样是"日常保洁",价格因城市、时长、房屋面积浮动,最忌讳把价格硬编码成单个字段。我在设计时拆了三张表:Service(服务基础信息)、ServiceSku(服务规格,包含时长、面积档位、价格)、Order(订单快照)。下单时会从ServiceSku里捞一份快照写入订单,后面就算运营改了价格,也不影响已下单的订单金额。这种对"历史数据不可变"的处理,是我在实际开发中觉得Django ORM最顺手的地方——模型继承、抽象基类、数据库迁移这些能力在做复杂业务时确实比手写SQL或轻量ORM更省心。

2.3 那Flask适合什么情况

Flask的优势是轻、灵活、自由度大。如果你做的只是一个内部工具、纯API后端、或者只需要一两个接口给小程序调用,Flask明显更合适。另外,如果团队对Python生态非常熟,喜欢自己组织项目结构和数据库层,Flask这种"能用就行"的框架反而更顺手。在家政预约平台这个场景里,Flask不是不能用,而是从"完整运营后台 + 多角色权限 + 订单流转"这些需求来看,Django的开箱即用组件更匹配。

另外要澄清一个常见的误区:Django和Flask并不是非此即彼的对立关系。我在这个项目里其实也保留了一个Flask写的"内部数据处理服务",专门跑定时任务,比如每天凌晨汇总前一天各区域订单量、计算家政人员月度评分。这类轻量脚本任务用Flask起一个独立服务非常轻快,和Django主服务共享同一个MySQL数据库,互不干扰。两套框架在同一个项目体系里各司其职,也是不少生产环境的真实做法。

2.4 Django REST Framework解决前后端分离的核心需求

既然是Vue前端 + Python后端,接口风格当然选RESTful。Django配合django-rest-framework(DRF)几乎是这个技术栈的事实标准。DRF的序列化器、视图集、路由注册、认证权限、频率限制全部都有现成组件,写接口的效率比纯手写JSONResponse高很多。

比如家政人员入驻时的技能标签提交,前端传一个ID列表,后端用PrimaryKeyRelatedField(many=True)直接关联到Skill表;订单创建接口用ModelSerializer做字段校验,非法状态值直接返回400。这些都是DRF的"常规操作"。如果你用Flask,这些都要自己在Flask-RESTful或蓝图里实现,不仅代码量更大,还容易出现参数校验遗漏的问题。

3. 数据库建模与预约状态流转的设计细节

数据库设计环节是整个项目中最"隐形的技术含量"。表面上看就是几张表、几个外键,实际上每张表的设计都会影响接口逻辑、并发安全甚至后期的数据统计准确性。我把自己最终的方案拆成三块来讲。

3.1 核心表结构一览

平台涉及的主体角色有三个:用户、家政人员、管理员。围绕预约核心业务,我设计了下面这些核心表:

表名核心字段说明
userid, nickname, phone, password_hash, avatar平台C端用户
housekeeperid, user_id, real_name, id_card, skills, service_area, status, rating家政人员资料,与用户表一对一关联
serviceid, category_id, name, cover, description, status服务项目,比如日常保洁、育儿嫂
service_skuid, service_id, spec_name, work_hours, price_unit, price服务规格,不同时长套餐
orderid, order_no, user_id, housekeeper_id, service_sku_id, address, appointment_start, appointment_end, amount, status, remark, create_time预约订单主表
order_status_logid, order_id, from_status, to_status, operator, create_time状态流转日志
reviewid, order_id, user_id, rating, content, create_time用户对已完成订单的评价
withdraw_applyid, housekeeper_id, amount, status, apply_time家政人员提现申请

这张表里最值得说的是order_status_log。采用"主表只存当前状态 + 日志表记录每一次流转"的双表设计,好处是任何时间点的状态变更都可以追溯。用户投诉说"我明明取消订单了为什么还扣款",运营人员一查日志表,取消操作有没有发生、发生在哪一秒、操作人是谁,一目了然。这个设计在真实项目中是刚需,不是可选项。

3.2 订单号生成与并发防重

订单号建议不要用数据库自增ID直接暴露给前端,一方面暴露业务量,另一方面也容易被爬虫遍历。我用的是"时间戳 + 用户ID后四位 + 3位随机数"拼成的20位字符串订单号,同时加了唯一索引兜底。虽然理论上随机数有极小概率冲突,但唯一索引会让冲突插入直接报错,代码里重试一次即可。

并发场景下的"重复下单"是这类平台的常见问题。用户快速点击"提交预约"两次,前端如果没有做按钮防抖,后端就会收到两个创建订单的请求。我在创建订单接口里做了一层幂等校验:同一个用户,对同一个服务SKU,预约开始时间相同,且订单状态为待支付的目标订单已存在时,直接返回已有订单,而不是新建。这个校验放到应用层解决不了数据库层的并发问题,所以我同时给order表加了一个联合唯一索引(user_id, service_sku_id, appointment_start),相当于上了双保险。

3.3 状态机设计,彻底避免"状态乱跳"

前面提到状态机,这里展开讲。家政预约的状态不是自由流转的,用户不能从"服务中"直接取消,家政人员不能把"待支付"变为"已完成"。我在Django应用目录下新建了order/state_machine.py,把合法流转表用配置字典写死:

ORDER_STATE_MACHINE = { "pending_payment": ["paid", "closed"], "paid": ["assigned", "refund_applying", "closed"], "assigned": ["in_service", "refund_applying"], "in_service": ["completed", "refund_applying"], "completed": ["reviewed"], "reviewed": [], "refund_applying": ["refunded", "assigned"], "refunded": [], "closed": [], }

对应地写了一个服务类,所有状态变更都通过它来完成:

class OrderStateMachine: @staticmethod def can_transit(current: str, target: str) -> bool: return target in ORDER_STATE_MACHINE.get(current, []) @staticmethod def transit(order, target, operator, extra=None): current = order.status if not OrderStateMachine.can_transit(current, target): raise InvalidStateTransition( f"订单状态不允许从{current}变更为{target}" ) # 执行状态变更副作用的钩子 OrderStatusLog.objects.create( order=order, from_status=current, to_status=target, operator=operator, remark=extra, ) order.status = target order.save()

为什么要这么"多此一举"而不是直接在视图函数里写order.status = "paid"?因为状态流转是系统里最容易出隐蔽Bug的逻辑。直接在视图里改字段,今天改这段代码、明天改那段代码,总有一天会出现一个绕过校验的非法流转。状态机集中管理后,新增一个流转路径只改一处配置,排查问题时也有明确的代码入口。我实际开发中因为早期图省事跳过状态机,结果"已取消"的订单被支付回调翻回"已支付"这种问题遇到过,后来彻底重构为状态机方案,这类问题归零。

4. Vue前端结构与关键交互实现

前端选型上,我用的是Vue 2.7 + Vue Router + Vuex + Element UI,这套组合在管理系统类项目里非常成熟,社区资料也全。家政预约平台的前端不只是管理后台,还包含用户操作界面,所以我把项目按"用户端"和"管理端"拆成了两个独立的前端工程,部署时用Nginx路径区分。

4.1 用户端的核心页面与组件拆分

用户端有五个核心页面:服务列表页、服务详情页、下单页、订单列表页、个人中心页。我把公共逻辑全部抽到组件和mixin里:

  • ServiceCard:服务项目卡片组件,接收一个service对象,展示封面、名称、起价、评分。
  • OrderStepper:下单流程步骤条组件,展示"选择服务 → 选择时间 → 填写地址 → 确认支付"四个步骤。
  • OrderStatusTag:订单状态标签组件,把后端返回的pending_payment等状态码映射成中文标签和对应颜色。

下单页是最考验交互设计的页面。用户需要选服务规格、选日期、选时段、填地址、填备注。这里有一个关键交互细节:时段的可用性状态需要实时从后端获取

具体来说,service_sku表里存储的只是一个"可预约时段模板",比如每天9:00-21:00每整点可约。但某个时段是否真的可约,还得看该时段是否已经被其他订单占用。我在前端下单页选择日期后,会调用一个接口:

GET /api/sku/{sku_id}/slots?date=2025-05-20

后端返回该日期每个时段的状态列表(可约 / 已约满 / 休息),前端把不可选的时段置灰。这个接口的实现核心是一个左连接查询:以时段表为主表,左连该日期下已生效的订单,有匹配记录则标记为已约满。

4.2 管理端:让运营人员真正愿意用起来

管理端我直接基于Vue + Element UI搭建。相比用户端的炫酷风格,管理端更讲究信息密度和操作效率。重点做了三个优化:

  • 订单列表页使用多条件组合筛选:状态、服务分类、日期范围、城市区域,筛选条件自动同步到URL query,刷新页面不丢筛选结果。
  • 所有"危险操作"(取消订单、退款审核、下架服务)都加了二次确认弹窗,并且在弹窗文案里明确告知该操作对用户端的影响。这一步虽然简单,但能有效减少运营误操作。
  • 数据统计页用ECharts展示每日订单量、营收趋势、各服务品类占比。我拉数据的接口设计成/api/admin/statistics/overview?start_date=xxx&end_date=xxx,一次性返回聚合好的JSON,避免前端做过多计算。

4.3 前后端联调中的关键约定

前后端分离最难的不是各自开发,而是联调阶段。我在项目里强制规定了几个接口规范,效果很好:

  • 统一响应格式:所有接口返回{ code: 0, message: "success", data: {...} },业务异常时code非0,前端axios拦截器统一弹错误提示。
  • 统一分页参数:列表接口统一使用pagepageSize,返回{ total, records },前端封装了pageRequest函数后不需要每个页面重写分页逻辑。
  • 时间统一传时间戳:前端和后端之间的时间传递全部用时间戳字符串,时区问题在联调中最容易出幺蛾子,传时间戳可以避免很多麻烦。

联调时我还习惯用PyCharm的HTTP Client功能写接口测试用例。在项目根目录建一个api_test.http文件,把常用的接口请求都写好,标记好环境变量,前端同学或者我自己调试时可以直接在IDE里点发送,比Postman切换环境更快,而且请求记录能跟着Git走。

5. 开发环境搭建与PyCharm配置要点

工欲善其事,必先利其器。家政预约平台这种Django + Vue的前后端分离项目,开发环境的搭建比写代码更影响效率。PyCharm是标题里的关键词之一,说明这套项目的典型开发姿势就是用PyCharm做主力IDE。我结合自己的配置经验,把关键步骤整理一下。

5.1 Python虚拟环境与Django项目初始化

很多人直接在系统Python环境里装包,这在新手阶段没问题,但项目一多依赖冲突就让人崩溃。我的建议是用venvconda创建独立的虚拟环境,PyCharm的Settings里可以直接配置Project Interpreter指向虚拟环境的Python解释器。

在PyCharm的Terminal里执行:

# 创建虚拟环境 python -m venv venv # 激活环境(Windows) venv\Scripts\activate # 激活环境(macOS/Linux) source venv/bin/activate # 安装Django及相关依赖 pip install django djangorestframework django-cors-headers mysqlclient pillow simpleui # 创建Django项目 django-admin startproject housekeeping_platform cd housekeeping_platform python manage.py startapp user python manage.py startapp order python manage.py startapp housekeeper python manage.py startapp admin_ops

这里有一个实际开发中的经验:不要把代码文件放在项目根目录裸奔,建议按功能拆分成多个app。Django的app机制本身就是一种模块化方案,每个app负责一块业务。我在项目里拆出了userorderhousekeeperserviceadmin_opscommon这6个app,模块边界非常清晰。

5.2 PyCharm运行配置与调试技巧

PyCharm里选中项目根目录,在Run/Debug Configurations里新增一个Django Server配置,Host填127.0.0.1,Port填8000。配置好后点Debug按钮,代码里打的断点会直接在浏览器请求触发时命中,这一点在处理订单状态流转Bug时特别好用。

调试Django时有一个我反复用到的技巧:在视图函数里对ORM的QuerySet求值前打上断点,然后在Debugger窗口里用"Evaluate Expression"随手执行orders.count()Order.objects.filter(user_id=2).first()这类探查语句,可以快速了解当前数据状态。比在代码里临时加print再删掉效率高得多。

另外,我在PyCharm里配置了实时模板来处理重复代码。比如创建REST接口的五件套——视图、序列化器、URL、权限判断、日志记录,做成一组Live Template,按键触发自动生成骨架,再人工补细节。对于这种模块边界清楚的项目,能有效减少写样板代码的时间。

5.3 Vue前端环境的安装与配置

Vue部分,我是用Vue CLI创建的工程。前置条件是Node.js环境,安装完后直接在PyCharm的Terminal里执行:

node -v npm -v # 全局安装Vue CLI npm install -g @vue/cli # 创建前端项目 vue create frontend

Vue CLI交互式创建过程中,我选择的是"Manually select features",勾选了Router、Vuex、CSS Pre-processors(选用SCSS)。项目创建完成后:

cd frontend npm install axios element-ui npm run serve

前端开发服务器默认跑在8080端口,后端Django跑在8000端口,两者端口不同必然有跨域问题。我使用django-cors-headers解决,在Django的settings.py里配置:

INSTALLED_APPS = [ # ... 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', # ... ] CORS_ALLOWED_ORIGINS = [ "http://localhost:8080", ]

开发阶段还可以直接启用CORS_ALLOW_ALL_ORIGINS = True图省事,但上线前一定要改回白名单模式。跨域配置看起来是个小问题,实际遇到时如果不清楚原理,会折腾很久。

5.4 PyCharm专业版与社区版的选择

结合标题里的"pycharm"关键词,我觉得有必要聊聊IDE版本。PyCharm社区版(Community)是免费的,对于纯Python开发完全够用,但前端Vue开发体验不足——社区版对JavaScript和Vue文件的语法高亮基本只有基础支持。PyCharm专业版对前端文件、数据库工具、HTTP Client都有完整支持,前后端分离项目里体验好很多。

如果你用的是社区版,前端代码我还是建议换用VS Code或者加上Vue官方插件来写。我自己日常习惯是:PyCharm专业版写Python后端,VS Code写Vue前端,两个IDE同时开,配合chrom浏览器的前端调试工具,开发效率最高。不要纠结于"用哪个工具是对的",能让你顺手推进项目的工具就是好工具。

6. 从开发到部署:服务器配置与常见坑

项目开发完成之后,上线部署是检验代码质量的最好方式。家政预约平台这种前后端分离架构,我推荐的部署方案是Nginx托管前端静态文件 + 反向代理后端API。一台2核4G的云服务器跑这套体系完全够用。

6.1 部署架构与流程

我整理了一下从服务器裸机到项目可访问的完整步骤:

  1. 安装Python 3.10+、MySQL 8.0、Nginx、git。
  2. 拉取后端代码到/home/www/housekeeping/backend,创建虚拟环境,安装依赖。
  3. 安装MySQL后创建数据库:
CREATE DATABASE housekeeping CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'house'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON housekeeping.* TO 'house'@'localhost'; FLUSH PRIVILEGES;
  1. 修改Django的settings.py,配置数据库连接、ALLOWED_HOSTS = ['你的域名或IP']DEBUG = False,收集静态文件。
  2. 使用uwsgi或者gunicorn启动Django。我用的是gunicorn
cd /home/www/housekeeping/backend source venv/bin/activate pip install gunicorn gunicorn -w 3 -b 0.0.0.0:8000 housekeeping_platform.wsgi:application
  1. 前端项目本地执行npm run build,生成的dist目录上传到服务器的/home/www/housekeeping/frontend,Nginx配置如下:
server { listen 80; server_name your_domain_or_ip; root /home/www/housekeeping/frontend; index index.html; # 前端路由history模式,找不到文件时回退到index.html location / { try_files $uri $uri/ /index.html; } # 后端API反向代理 location /api/ { proxy_pass http://127.0.0.1:8000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # Django静态文件 location /static/ { alias /home/www/housekeeping/backend/static/; } }

这里有一个前端路由history模式的关键知识:Vue Router以history模式运行时,直接访问/order/list路径时,Nginx会找不到对应的真实文件,必须加上try_files $uri $uri/ /index.html把请求回退到前端入口,否则会出现"刷新页面就404"的问题。

6.2 部署环节最常见的五个问题

问题1:MySQL客户端库装不上。

pip install mysqlclient在Linux服务器上经常报错,因为缺少编译依赖。解决方法是在服务器上先执行:

yum install python3-devel mysql-devel gcc -y

装好编译依赖后,mysqlclient就能正常安装了。如果你嫌折腾,也可以用pymysql作为替代,在Django项目__init__.py里写入:

import pymysql pymysql.install_as_MySQLdb()

问题2:Nginx代理后接口超时。

订单创建后需要同步计算价格、检查时段冲突,如果数据库查询慢,接口容易超过Nginx默认的60秒超时时间。排查思路是先看后端日志确认是不是接口本身慢,其次在Nginx的location /api/里加上proxy_connect_timeout 60s; proxy_read_timeout 60s;

问题3:上传的头像图片无法显示。

用户头像上传后存储在本地的/media/目录,但Nginx没有给/media/路径配反向代理,导致图片404。在Nginx配置里加上:

location /media/ { alias /home/www/housekeeping/backend/media/; }

同时Django的settings.py要确保MEDIA_URL = '/media/'MEDIA_ROOT配置无误。

问题4:Django的ALLOWED_HOSTS没配置导致访问报400。

部署阶段最容易忽略的就是这个,我在这里卡过不止一次。只要用域名或IP访问,都需要把域名/IP加入ALLOWED_HOSTS列表,否则Django会直接拒绝请求。

问题5:前端打包后接口地址写死成localhost。

很多同学开发时在.env.development里配置VUE_APP_BASE_URL=http://localhost:8000/api,上线打包时忘了新建.env.production并修改地址,导致线上页面请求走了开发地址。我建议打包前先检查代码里是否有残留的localhost,最好全程使用环境变量:

# .env.production VUE_APP_BASE_URL=/api

Nginx把/api/反向代理到后端,前端到处用process.env.VUE_APP_BASE_URL拼接请求路径,部署和本地开发都不需要改代码。

6.3 数据库迁移数据导入的实操

项目迭代过程中,经常会遇到需要修改表结构的情况。Django的迁移机制会生成migrations目录下的迁移文件。上线时在服务器上执行:

python manage.py makemigrations python manage.py migrate

不过有一个执行顺序的坑:跨环境的迁移文件如果版本顺序对不上,migrate可能会报"Migration dependencies reference nonexistent parent node"之类的错误。我的处理策略是:修改模型后先在本地验证迁移可以正常生成和执行,然后连同迁移文件一起提交到Git,服务器拉取代码后只执行manage.py migrate,不执行makemigrations——服务器的职责是执行迁移,而不是生成迁移。

如果需要把本地开发数据同步到服务器,可以用Django的dumpdataloaddata命令,但要注意外键关联的顺序问题。我更推荐用MySQL原生的mysqldump工具,在本地导出再在服务器导入:

mysqldump -u root -p housekeeping > backup.sql scp backup.sql user@server:/tmp/ mysql -u root -p housekeeping < /tmp/backup.sql

注意:不要在生产数据库上直接用loaddata覆盖已有数据,尤其是包含用户支付信息、订单流水这些重要数据时,一定要先备份,再用增量逻辑导入。

7. 接口安全与权限控制实战

家政预约平台涉及用户手机号、住址、订单记录这些敏感数据,接口安全这块不能对付。我按"认证 → 授权 → 传输加密"三层来落实。

7.1 用户认证方案:JWT还是Session

传统Django模板开发常用Session认证,但前后端分离后Session的Cookie管理比较繁琐,跨域场景下还要维护CSRF token。我选用的是JWT方案,结合djangorestframework-simplejwt库实现。

核心配置如下:

# settings.py REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', ], 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.IsAuthenticated', ], } from datetime import timedelta SIMPLE_JWT = { 'ACCESS_TOKEN_LIFETIME': timedelta(hours=2), 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), 'ROTATE_REFRESH_TOKENS': True, }

用户登录成功后,后端返回access_tokenrefresh_token,前端把access_token存到localStorage或内存中,每次请求在axios拦截器里加上Authorization: Bearer <token>access_token有效期设2小时,过期后用refresh_token换新的access_token,不需要用户重新登录。这个方案的优点是无状态,后端不需要存Session记录,集群部署也方便。

JWT方案的安全风险点是:token一旦签发,在有效期内无法在服务端主动作废。用户修改密码、被管理员封禁时,旧的access_token仍然有效。我在实际项目中做了妥协处理:权限校验时先看用户状态字段,如果用户已被禁用,直接拒绝请求,相当于用"业务层校验"弥补了"无状态token"的短板。

7.2 水平权限控制:防止越权操作

权限系统只做到"登录用户才能访问"远远不够。家政预约平台里,用户A不应该看到用户B的订单,家政人员C只能操作自己的接单状态。这种水平权限控制需要在每个接口里显式处理。

DRF框架的get_queryset是干这个事的最佳位置。比如订单列表接口:

class OrderViewSet(viewsets.ModelViewSet): serializer_class = OrderSerializer def get_queryset(self): user = self.request.user if user.role == 'admin': return Order.objects.all() return Order.objects.filter(user=user)

对象级权限用get_permissions配合自定义权限类:

class IsOrderOwnerOrAdmin(BasePermission): def has_object_permission(self, request, view, obj): return obj.user == request.user or request.user.role == 'admin'

这样一个order的黑客不管怎么修改订单ID,都拿不到别人的订单数据。很多人做毕设或者内部项目不重视数据权限,出了问题才后悔,安全意识这方面宁可过度设计也不要图省事。

7.3 输入校验与SQL注入防护

Django ORM本身做好了SQL参数化,直接拼接原生SQL的场景极少,所以SQL注入风险可控。真正需要警惕的是业务层面的输入异常

地址字段是最典型的。用户下单时填的家庭地址是自由的文本,过滤XSS脚本需要在前端做一层,后端也要处理。我的做法是在后端序列化器里对地址字段做一个清洗:

import re def clean_address(value): # 移除可能的脚本标签 value = re.sub(r'<[^>]*>', '', value) return value.strip()

另外,价格字段不要从前端直接传。订单金额应该由后端根据service_sku_id从数据库查价格计算,前端只能提交SKU ID、服务时间段和地址。如果让前端传金额,用户拿工具改一下请求体,就能以1分钱下单,这是必须堵住的漏洞。

8. 家政平台的可视化统计与运营报表

家政预约平台上线后,运营团队最关心的是三个问题:本周订单量涨了还是跌了?哪个服务品类最受欢迎?各区域的家政人员产能是否饱和?这些问题都需要数据支撑,所以在系统里加一个可视化统计模块非常有必要。

我采用的是"后端聚合出JSON + 前端ECharts渲染"的方案,按"平台总览"和"家政人员个人战绩"两个维度来设计。

8.1 平台总览报表

后端统计接口的核心SQL逻辑大概是:

from django.db.models import Count, Sum from django.db.models.functions import TruncDate def get_daily_order_stats(start_date, end_date): return ( Order.objects .filter(create_time__date__gte=start_date, create_time__date__lte=end_date) .annotate(day=TruncDate('create_time')) .values('day') .annotate(order_count=Count('id'), total_amount=Sum('amount')) .order_by('day') )

前端拿到这个数组,直接塞给ECharts的折线图,一天订单量的趋势就出来了。我在页面上还加了三个KPI卡片:今日订单量、本月营收、待派单数量,让运营一打开后台就能看到核心数字。

这个模块要注意的坑是:直接用create_time__date做查询时,如果数据量大,会很慢。我的优化策略是:在Order表增加一个冗余字段order_date(日期类型),订单创建时直接写入当天日期,日常查询全部走这个字段并加索引,统计性能能提升一个量级。

8.2 家政人员绩效排名

运营需要知道哪个家政人员订单完成率最高、评价最好。我用DRF + 子查询实现了一个简单的榜单接口:查询所有家政人员在指定时间范围内的已完成订单数和平均评分,降序返回前20名。这个功能虽然做起来不难,但对运营的实际价值非常高——续约哪些人员、淘汰哪些人员,数据一目了然。

表结构上,review表和order表通过order_id关联,因为每单最多一个评价,所以可以做一次联表后聚合:

from django.db.models import Avg, Count top_housekeepers = ( Housekeeper.objects .filter(order__status='completed', order__update_time__range=(start, end)) .annotate(completed_count=Count('order')), .annotate(avg_rating=Avg('order__review__rating')) .order_by('-completed_count', '-avg_rating')[:20] )

前端我用一个排行榜样式的Table展示:前三名高亮显示,后面用灰色。这个功能简单但要细心处理空值:新入驻的家政人员还没有评价数据,avg_rating会返回None,前端要显示"暂无评分"而不是渲染出错。

8.3 用ECharts展示热门服务品类

最后一个是服务品类的销售占比,我选用了ECharts的饼图。后端接口设计为返回每个服务分类的订单数和营收占比,前端传递两个数组给饼图组件就完成了。

这个统计模块让运营和管理者在宏观层面快速掌握平台运转状况,同时也让整个家政预约平台从"能跑"进化到"能给业务提供决策支持"。对一个真实要上线运营的平台来说,这部分功能不是锦上添花,而是必需品。


我在实际开发中最受益的一条经验是:在动手写第一行代码之前,先画清楚状态流转图和数据表关系图。家政预约平台的复杂度不在某个页面的UI,而在订单状态的流转、数据的一致性保障、多角色权限的隔离,这些设计层面的功课做到位了,后面的编码只是按图索骥。如果你正在用Python和Vue做类似的项目,希望这篇的内容能帮你少走一些弯路。有任何具体的实现细节或者卡壳的地方,欢迎交流讨论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询