☰
Python+Vue+Django家政系统实战:前后端分离架构与订单状态流转
2026/10/9 3:24:39 网站建设 项目流程

做管理系统这些年,我从最早用PHP拼后台,到现在主栈用 Python 写接口、Vue 画页面、PyCharm 当主力 IDE,绕了不少弯路。今天拿一个比较典型的“家政服务管理系统”当例子,把完整的技术决策过程、核心代码怎么落、前后端怎么联调,以及一堆踩坑经验一次性摊开讲清楚。这套项目管理系统的技术栈是 python + vue + django/flask + pycharm,也正是后台管理系统里很常见的一套组合,业务逻辑不复杂,但麻雀虽小五脏俱全,非常适合拿来练手或直接改造成商用原型。

在讲具体实现之前,先给结果定个调:这个系统最难的部分不是写代码,而是把“订单状态流转”和“人员权限边界”想明白。代码只是把流程固化下来而已。你把这两件事理清了,剩下的 CRUD 基本就是体力活。

1. 项目定位:家政服务管理系统到底要解决什么问题

1.1 业务场景与现实痛点

家政服务行业覆盖面很宽,日常保洁、月嫂、育儿嫂、老人陪护、家电清洗都算。这些业务在线下的真实状态通常是:客户打电话或发微信下单,客服手动记在表格里,然后群里喊阿姨接单,做完之后服务质量和客户反馈很难追踪。问题集中在这几个地方:

  • 客户下单没有标准化入口,全靠人工沟通,信息容易丢。
  • 派单过程不透明,管理员不知道哪位服务人员有空,客户也不知道进度。
  • 订单数据散落,查历史订单、算服务人员工资都费劲。
  • 缺少评价机制,服务质量好坏全凭口碑。

管理系统要做的,就是把这些线下流程搬到线上,形成“客户下单 → 管理员派单 → 服务人员接单 → 客户验收评价”的闭环。

1.2 用户角色与核心功能清单

这个系统围绕三类角色展开。客户看到的是服务浏览、在线下单、订单状态、评价;服务人员看到的是接单列表、个人排班、历史收入;管理员拿到的是全量订单、人员管理、服务项目配置和数据统计。三类角色的权限边界必须一开始就划清楚,否则后面加权限控制就是给自己挖坑。

功能优先级我建议按 MVP 思路排:先把能形成业务闭环的做出来,再补管理功能。

模块核心功能优先级
用户认证注册、登录、Token签发与刷新高
服务展示服务分类、价格、详情高
预约下单选服务、选时间、填地址、生成订单高
订单管理状态流转、订单列表、订单详情高
服务人员管理人员信息、技能标签、状态维护中
评价反馈服务完成后打分、写评价中
后台统计订单量、收入、服务人员排行低

我见过不少人一上来就堆功能,结果核心流程还没跑通,先把自己累死。这套系统一定要先通“下单-派单-完成”的主干道,剩下的都是枝叶。

2. 技术选型:Django、Flask、Vue 到底怎么定下来

2.1 Django 与 Flask、FastAPI 的核心差异

标题里同时出现了 Django 和 Flask,说明不少人在两者之间纠结过。我两个框架都用过,说点实在的对比。

Django 是“全家桶”思路,自带 ORM、Admin 后台、认证系统、表单、迁移工具。做一个信息管理系统,它自带的这些能力正好打在需求点上:我建一张表就是写一个模型类,管理后台甚至不用自己写页面,Admin 直接能顶一半的运营需求。Flask 是“微框架”思路,核心只做路由和基础服务,ORM 用 SQLAlchemy 拼,Admin 用 Flask-Admin 拼,用户认证用 Flask-Login 拼,什么都要自己选型,灵活但决策成本高。

还有热词里提到 FastAPI。FastAPI 性能好、自带接口文档,但它更适合高并发 API 服务,放到这种业务逻辑密集的管理系统里,异步优势体现不出来,生态反而不如 Django 成熟。

2.2 为什么最终选择 Django + Vue 的组合

选 Django 的核心原因是它解决了一个很实际的效率问题:CRUD 量大的管理后台,Django 的 ORM 和 ModelSerializer 能省掉大量样板代码。写一个服务分类的增删改查,在 Django 里可能只需要一个 ViewSet 加三行路由。

前端选 Vue 而不是用 Django 模板渲染,关键考量是交互体验和接口复用。客户下单页、订单状态实时刷新、管理员拖拽式派单,这些交给 Vue 响应式处理比刷新页面自然得多。更重要的是,API 做成了纯接口之后,后续如果要出小程序端或者 App 端,后端一套接口直接复用,不需要再写一遍。

有人问过我,Django 自己就能渲染页面,为什么非要前后端分离?我的看法是:Django 模板渲染适合“页面少、交互轻”的官网,家政这种带实时状态、多角色视图的业务系统,前后端分离才是长期省力的方案。

2.3 PyCharm 在这套组合里的角色

PyCharm 在这类项目里不是可有可无的编辑器,它承担了几个关键能力。首先是虚拟环境管理,Project Interpreter 直接指向项目的 venv,不用手工切环境;其次是 Run Configuration,一个配置启动 Django,一个配置启动前端 npm run dev;再有就是自带数据库面板,能直接查看 MySQL 表结构和查询日志,排查接口问题时效率特别高。PyCharm Professional 对 Vue 的支持更完整,识别 .vue 文件语法,ESLint 集成也不用额外配终端。社区版虽然免费,但前端支持弱一些,如果预算允许,专业版体验会好很多。

3. 环境搭建与项目初始化

3.1 Python、Django、虚拟环境的准备流程

版本是我的老建议:Python 用 3.10 或 3.11,Django 用 4.x LTS。太新的 Python 版本有时第三方库还没跟上,太老的又会碰到语法兼容问题。

虚拟环境一定要建。很多新手图省事直接 pip install 到全局环境,后面包版本装乱了就知道痛了。操作流程:

mkdir home-service cd home-service python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate pip install django djangorestframework djangorestframework-simplejwt django-cors-headers

装完确认版本:

python -m django --version

项目结构和应用拆分建议按业务域来:

django-admin startproject home_service cd home_service python manage.py startapp users python manage.py startapp services python manage.py startapp orders python manage.py startapp workers python manage.py startapp reviews

每个 app 只负责自己的业务域,用户归用户、订单归订单。这样拆的好处是后面出问题定位代码快,不用一个 app 里翻几百行。

3.2 Vue 3 项目创建与环境配置要点

前端部分需要 Node.js 环境和包管理器。Node 版本别用太新的,我用 18 LTS 比较稳。创建项目我推荐用 Vite 而不是 Vue CLI,Vite 的启动速度和热更新体验明显更好,而且 Vue 生态现在全面转向 Vite。

npm create vue@latest frontend cd frontend npm install npm install axios pinia vue-router

需要注意一个点:npm create vue@latest 创建出的模板,如果选 TypeScript,会比较依赖 @vue/tsconfig 这类包。热词里提到的 failed to load tsconfig 就是版本不匹配导致的,如果不想折腾,直接用 JavaScript 模板最省心。我自己的习惯是这类业务系统用 JS 就够了,类型约束主要靠接口文档和手动规范,不必要为 TS 增加复杂度。

3.3 PyCharm 关联项目与启动配置

项目建好后,需要把后端项目和前端项目都挂到 PyCharm 里。步骤是:File -> Open 打开后端目录,确保 Settings -> Project -> Python Interpreter 指向刚才创建的 venv。之后在前端目录执行 npm install,PyCharm 会自动识别 package.json。

后端调试配置这样设:Run -> Edit Configurations -> 新建 Django Server,Host 填 127.0.0.1,Port 填 8000。前端不需要在 PyCharm 里额外建 npm configuration,直接用终端跑 npm run dev 也行,但我建议建一个 npm 配置,方便在 IDE 内一键启动和查看日志。

4. 系统架构与数据库设计

4.1 前后端分离架构与请求链路

整套系统的请求链路是这样的:Vue 组件里用户操作产生事件,Axios 发出 HTTP 请求,开发时请求交给 Vite 的 dev server 代理转发,到达 Django 的 API 路由;Django 通过 DRF 的 ViewSet 接收请求,反序列化参数,经过 ORM 查询 MySQL 数据库,返回 JSON 数据;Vue 收到数据后更新响应式状态,重新渲染页面。

这套链路里有一个关键配置需要提一下,就是开发和生产的代理策略。开发时用 Vite proxy;生产环境用 Nginx,静态页面托管在 Nginx,/api 路径反向代理到 Django 服务进程。这个模式前后端分离的标准做法,不要在生产环境直接暴露 Django 的 8000 端口,处理静态文件效率和安全性都不行。

4.2 数据库模型设计

我建议数据库实体设计成五张核心表:用户表、服务类型表、家政人员表、订单表、评价表。用户表直接继承 Django 的 AbstractUser,加一个 phone 字段和 role 字段,role 用“客户/服务人员/管理员”三选一的 choices。这么设计比单独建两张表更省事,因为三种角色都要登录认证。

服务类型表比较简单,就是 name、desc、icon、price、unit 字段。家政人员表稍微要注意的是它要单独维护,不和用户表强行一一对应,因为同一个服务人员可能有多个用户账号或者被跨门店调度,我用外键关联加技能标签的方式来处理。

订单表是核心,字段包括:订单号、用户外键、服务类型外键、家政人员外键、预约开始时间、服务地址、状态、备注、金额。重点是状态字段,我用 IntegerField 而不是字符串,配合 choices 做枚举,避免脏数据。这么设计的好处是,Django 的查询语法可以直接按数字过滤,效率高且不容易写错。

评价表就是 order 外键加 rating、content、created_at。订单完成后才能填写评价,这个逻辑在后端接口里要校验订单状态。

4.3 API 接口规划

接口风格用 RESTful,后端用 DRF 的 ModelViewSet 自动生成路由。

方法路径说明权限
POST/api/auth/login/登录获取 Token公开
POST/api/auth/register/注册公开
GET/api/services/服务列表登录
GET/api/services/{id}/服务详情登录
POST/api/orders/创建订单客户
GET/api/orders/my/当前用户的订单登录
GET/api/orders/全部订单管理员
PATCH/api/orders/{id}/更新订单状态管理员/服务人员
GET/api/workers/家政人员列表登录

路由规划的原则是:按角色能看到什么来决定接口开放程度,而不是一个接口通用到底。

5. 后端核心功能开发实战

5.1 用户认证与 Token 鉴权方案

前后端分离场景下,Session 方案已经不适用了,我用 SimpleJWT 做 Token 认证。核心配置在 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(days=1), 'REFRESH_TOKEN_LIFETIME': timedelta(days=7), }

登录接口可以直接用 SimpleJWT 自带视图处理,但业务系统里登录之后往往要带上用户信息,所以我更习惯自定义一个视图,登录成功后返回 token、refresh 和用户基础信息。前端把 token 存 localStorage,每次请求在请求头里带 Authorization: Bearer 。Token 过期的处理是响应拦截器里捕获 401,然后尝试用 refresh token 刷新,刷新失败就跳到登录页。

5.2 Django 执行查询与删除对象的正确姿势

热词里反复出现 django 查询和删除,这是新手最容易出错的地方。先看一个反面典型:

# 错误示范:对象不存在时直接抛异常 order = Order.objects.get(id=order_id) order.delete()

如果订单不存在,get 抛 Order.DoesNotExist,接口直接 500。正确做法是先用 filter 或者捕获异常:

# 方案一:先查再删 try: order = Order.objects.get(id=order_id) except Order.DoesNotExist: return Response({'error': '订单不存在'}, status=status.HTTP_404_NOT_FOUND) order.delete() # 方案二:批量删除 deleted_count, details = Order.objects.filter(status=2).delete()

这里补充一个批量细节:filter 的 delete 返回一个元组,第一个值是总共删除的行数,第二个值是各级联表的具体删除数。如果你的外键设置了 on_delete=models.CASCADE,批量删除会把关联的评价表数据也一并删掉。

不过我要给个重要提醒:业务系统里,订单这种核心数据不要做硬删除。理由很简单,客户交易记录、维权凭证、服务人员工资结算全都要依赖历史订单数据。我用的方案是软删除,给订单表加一个 is_active 字段,默认 True,删除操作其实就是把 is_active 改成 False,查询时默认过滤掉。这样既保证了业务安全,也不影响“已删除”订单的数据追溯。

5.3 预约下单与订单状态流转实现

订单状态流转是这套系统的核心业务逻辑,状态定义如下:

class OrderStatus: PENDING = 0 # 待派单 ASSIGNED = 1 # 已派单 IN_PROGRESS = 2 # 服务中 COMPLETED = 3 # 已完成 CANCELED = 4 # 已取消

状态迁移规则要写死在更新逻辑里,不能每个接口各写各的。比如只有待派单状态可以被取消,只有已完成状态可以创建评价。我把状态更新收敛到一个方法里:

def update_order_status(order, new_status, user): allowed_transitions = { OrderStatus.PENDING: [OrderStatus.ASSIGNED, OrderStatus.CANCELED], OrderStatus.ASSIGNED: [OrderStatus.IN_PROGRESS, OrderStatus.CANCELED], OrderStatus.IN_PROGRESS: [OrderStatus.COMPLETED], } if new_status not in allowed_transitions.get(order.status, []): raise ValidationError('非法的状态变更') order.status = new_status order.save() return order

如果你不想在代码里硬编码这个迁移表,可以引入 django-fsm 这类状态机库,但我个人建议业务就这么几个状态时,别为了用库而用库,一个字典表就够了,可读性还更高。

5.4 数据序列化与接口权限控制

后端接口用 DRF 的 ModelViewSet 写起来确实快,但权限控制一定要做细。我一般重写 get_permissions 方法,按 action 分配权限:

class OrderViewSet(viewsets.ModelViewSet): serializer_class = OrderSerializer queryset = Order.objects.filter(is_active=True) def get_permissions(self): if self.action == 'create': return [IsAuthenticated()] if self.action == 'list': if self.request.user.role == 2: return [IsAdminUser()] return [IsAuthenticated()] return [IsAuthenticated()]

同时要把列表查询重写一下,区分角色看到的订单范围:客户看自己的单,服务人员看派给自己的单,管理员看所有单。

5.5 Django Admin 与运营后台配合

Django Admin 不要浪费掉。它在项目初期和运营后台不完善时,能极大地解放开发时间。把三个核心模型注册进 Admin,配置 list_display、list_filter、search_fields,管理员日常维护服务项目和查看订单,在 Admin 里就能完成。等业务量上来、运营后台需要和客户系统打通时,再在前端 Vue 里实现管理页面也不迟。

6. Vue 前端页面开发实录

6.1 路由与页面结构规划

前端路由用 Vue Router,页面组件按视图划分:首页、服务列表、预约下单、我的订单、订单详情、登录、后台管理。路由配置用懒加载,避免首屏加载过重:

const routes = [ { path: '/', component: () => import('@/views/Home.vue') }, { path: '/services', component: () => import('@/views/ServiceList.vue') }, { path: '/booking', component: () => import('@/views/Booking.vue') }, { path: '/orders', component: () => import('@/views/OrderList.vue') }, { path: '/login', component: () => import('@/views/Login.vue') }, { path: '/admin', component: () => import('@/views/AdminDashboard.vue') }, ]

路由守卫里统一处理登录态和角色权限。没登录访问 /orders 就跳登录页,管理员访问普通页面不需要拦截,普通用户访问 /admin 直接提示无权访问。路由守卫这块写在前端的好处是客户体验更友好,服务端接口权限仍然是真正的安全屏障。

6.2 插槽与组件复用的实战案例

Vue 3 的插槽机制在服务卡片这类组件里特别好用。核心思路是:组件的结构和样式固定,但其中的操作按钮由父组件传入,这样同一个卡片组件既能用在服务列表页,也能用在用户后台。

<!-- ServiceCard.vue --> <script setup> defineProps({ service: Object }) </script> <template> <div class="service-card"> <img :src="service.icon" :alt="service.name" /> <h3>{{ service.name }}</h3> <p>{{ service.description }}</p> <span class="price">¥{{ service.price }}/{{ service.unit }}</span> <slot name="footer" :service="service"></slot> </div> </template>

父组件这样传内容:

<ServiceCard v-for="item in services" :key="item.id" :service="item"> <template #footer="slotProps"> <button @click="handleBook(slotProps.service)">立即预约</button> </template> </ServiceCard>

这样设计后,服务列表页传“立即预约”,后台管理页可以传“下架”“编辑”,卡片本身不用改一行代码。

6.3 Axios 二次封装与联调配置

Axios 我建议封装成一个统一请求模块,好处是 token 注入、错误处理、loading 状态这些通用逻辑只写一次:

import axios from 'axios' const request = axios.create({ baseURL: '/api', timeout: 10000, }) request.interceptors.request.use(config => { const token = localStorage.getItem('access_token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) request.interceptors.response.use( response => response.data, error => { if (error.response?.status === 401) { localStorage.removeItem('access_token') window.location.href = '/login' } return Promise.reject(error) } ) export default request

开发环境下还需要在 Vite 配置文件里配好代理:

// vite.config.js export default defineConfig({ server: { proxy: { '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true, }, }, }, })

配了代理之后,前端代码里写 /api 开头的请求就不会遇到跨域问题了。生产环境下,Nginx 再做一层同样的转发规则。联调阶段最容易出的问题就是 token 过期没有全局处理、错误提示不统一,封装好请求模块后能规避大部分。

6.4 订单提交与页面状态联动

下单页面是用户操作最集中的页面,包含服务类型选择、服务时间选择、地址填写、备注、价格计算以及提交。提交前要校验表单完整性和时间格式,提交中的 loading 状态要防止重复点击。提交成功之后跳转到订单列表页,列表页根据订单状态字段展示对应操作按钮:待派单的可以取消,服务中的可以联系客服,已完成的可以去评价。这些状态联动本质上就是前端把后端返回的 status 字段映射成 UI 的“下一步动作”,映射关系建议写成配置对象,不要散落在各处 if/else。

7. 开发过程中踩过的坑与排查技巧实录

7.1 跨域问题:Django 配置与 Vite 代理的配合

前后端分离最容易撞的就是跨域。症状很典型:浏览器控制台报 CORS policy 错误,接口请求发不出去。解决办法有两层。开发环境下,Vite proxy 把 /api 转发到 Django 服务器,浏览器只认识前端的域名,不会触发跨域;生产环境下,Nginx 反向代理同理。如果你确实需要跨域调用,再配置 django-cors-headers:

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

我的建议是别同时依赖 proxy 和 CORS,开发时用一个方案就行,否则两套配置容易互相干扰,排查问题反而更麻烦。

7.2 Django migrate 冲突与迁移文件管理

migrate 出问题多数是多个开发分支改同一个模型造成的。报错一般是“Migration admin.0001_initial dependencies reference nonexistent parent node”。网上搜到的土办法是删掉数据库和迁移文件重建,但生产环境不可能这么干。我的排查思路是先用 makemigrations --check 看有没有未生成的迁移,再检查应用的 migrations 目录有没有损坏的依赖关系。如果确认是迁移历史混乱,正确做法是新建一个空的迁移文件把当前状态归零,而不是直接删库。

python manage.py makemigrations --check --dry-run python manage.py migrate app_name --plan

7.3 Vue 依赖版本兼容问题与 Node 版本选择

热词里那条 failed to load tsconfig 我在实际项目里遇到过。多半是 @vue/tsconfig 版本升级后,项目里的 tsconfig.json extends 路径变了。如果你是 TypeScript 模板,建议把 Vue 相关依赖的版本锁定,用 package.json 里的完整版本号而不是 ^ 前缀。这个坑在 JavaScript 模板里不会有,所以我也再次建议这类系统优先用 JS。

另一个高频问题是 Node 版本过旧导致 Vite 启动报错。Vite 5 对 Node 版本有要求,一般是 18+,过低会报“crypto" is undefined之类,升级 Node 就能解决。

7.4 常见问题速查表

症状可能原因解决方法
接口 500 且日志有 DoesNotExistget() 未捕获对象不存在改用 get_object_or_404 或 try/except
前端登录后刷新页面又跳回登录页Token 存取位置不对或过期统一存 localStorage,响应拦截器加刷新逻辑
Pillow 安装失败Python 版本与 Pillow 版本不兼容pip install pillow 指定版本,或升级 Python
数据中文乱码数据库字符集不是 utf8mb4创建数据库时指定 CHARSET=utf8mb4
npm install 后启动报版本错误依赖互相不兼容删 node_modules 和 lock 文件重装
Django 后台样式丢失DEBUG 设为 False 后静态文件没有收集执行 collectstatic 并配 Nginx 静态目录

7.5 过来人的几条总结构思

按我的经验,这类系统开发中最贵的返工往往发生在需求没锁定时就急着写代码。家政业务的订单状态和权限边界,最好先花半天画一张状态流转图和角色权限矩阵,再动手。另外开发环境尽量保持简单,数据库先用 SQLite 跑通业务流程,最后切 MySQL,能省掉很多环境干扰因素。第三,接口联调时前端先 mock 数据,后端先保证接口文档准确,不要两边同时开发又同时改,不然 bug 归属都分不清。

8. 部署上线与后续扩展建议

8.1 简单可靠的部署策略

Django 后端部署用 Gunicorn 进程管理,前端构建出的 dist 目录交给 Nginx。架构就是一个 Nginx 对外服务,/ 路径指向前端静态文件,/api 路径反向代理到 Gunicorn。

# 前端构建 cd frontend && npm run build # 后端启动 pip install gunicorn gunicorn home_service.wsgi:application -w 4 -b 0.0.0.0:8000

Nginx 配置里注意两点:gzip 压缩开起来,对 JSON 和 JS 文件收益明显;/api 转发时要把 Host 头带上,否则 Django 的 CSRF 或者站点校验可能出问题。

正式部署前,把 settings.py 里的 SECRET_KEY、DEBUG、ALLOWED_HOSTS 都调成生产配置。关掉 DEBUG 后,静态文件服务和错误页面就得靠 Nginx 和 Django 的错误收集机制来兜底。

8.2 这个系统还能往哪些方向扩展

基础版本跑通后,可以加的功能很多:对接微信支付或支付宝支付,让客户在线完成付款;增加地图选服务人员的交互,用高德地图或者 Mapbox 的 Vue 版本组件;后台增加订单量、收入、人员评价的统计图表;再进一步,可以用消息推送通知客户订单状态变更。这些扩展方向后端接口都是现成的,前端新增页面就行。如果未来要出小程序端,Django REST API 本身就是一套,直接复用即可,这部分前期坚持前后端分离的价值就体现出来了。

我从这个项目里最深的体会是:管理系统开发,代码量不是门槛,业务的确定性才是。你把订单状态、角色权限这些业务规则想清楚了,Django 加 Vue 这套组合真的能打得很轻巧。如果你正在用这套技术栈做类似系统,建议先把状态流转图画出来,再动手写代码,能省掉的返工不是一点半点。

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

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

立即咨询