Django+微信小程序设备报修管理系统全栈实现指南

发布时间:2026/10/6 19:06:01
Django+微信小程序设备报修管理系统全栈实现指南 设备报修这事看起来是个小功能真做起来才知道有多少场景要抠谁报修、报给谁、设备在哪、坏成什么样、有没有图片、派给谁修、修完怎么验收、超时没处理怎么提醒。我经手过好几个类似的项目最怕的就是企业里还在用纸质单子和微信群接龙一两个月后想统计哪台设备老坏数据全躺在聊天记录里。所以当有个项目要做一个“微信小程序 Django 后端”的设备报修管理系统并且把 Node.js 也放进整套工具链里时我第一反应是这组合靠谱但一定要先把技术定位理清楚。这篇内容适合正在做毕业设计、企业内部小工具、或者刚接触 Django 和小程序全栈开发的人参考。我会从项目拆分、环境配置、后端设计、小程序前端、联调部署到避坑记录完整讲一遍这套设备报修管理系统的实现思路。核心关键词是nodejs、django、微信小程序、设备报修管理系统。你不需要有一堆经验只要跟着把环境装好、把代码跑通再按自己的业务去改就能落地一个真正能用的系统。1. 项目整体设计为什么是这套技术栈1.1 先把业务需求拆明白做管理系统最忌讳一上来就写代码。设备报修听起来只有一个“报修”动作但认真拆下来至少有四类角色、五条状态流转、三个核心页面。普通用户微信扫码或搜索小程序查看自己上报的工单提交报修单。维修员看被分派给自己的工单更新处理进度填写维修结果。管理员管理设备台账、报修分类、分配维修员、撤回或驳回工单。系统后台记录报修时间、响应时间、完成时间方便后续生成统计报表。所以在设计阶段我先把状态机画清楚注意这里说的“状态机”是指业务状态流转比如“待派单→已接单→维修中→已完成→已驳回”。这条链路是整个系统的灵魂后端所有权限判断、前端所有按钮显隐都围绕状态机走。项目里后面用的主状态是pending / assigned / repairing / completed / rejected对应中文就是待派单、已接单、维修中、已完成、已驳回。有些业务还要加“待评价”“已取消”这个可以按你实际需求去扩展但核心五个状态已经够用。1.2 Django 在后端担当什么角色Django 在这套系统里是真正的服务端核心。它不是只做 CRUD 那么简单而是承担了用户认证、设备台账、工单流转、权限控制、图片上传、后台管理等功能。为什么选 Django我自己的体验是它自带的 ORM 和数据模型迁移机制能让我在改字段时不用背 SQL自带的 admin 后台在开发阶段几乎免费给了我一个管理界面用户认证体系和权限装饰器也都能直接复用。再加上 Django REST FrameworkDRF来做接口开发写一个接口的大部分工作都变成了“定义序列化器 写视图逻辑”效率非常明显。用 Django 还有一个隐藏优势它有非常成熟的数据库事务、查询优化和导出能力。设备报修系统后期一定免不了做统计报表比如“哪一类设备故障最多”“哪个维修员接单最多”“平均响应时长多少”Django 的 ORM 配合聚合函数可以很快写出来。如果用的是其他后端框架这些统计逻辑往往要自己拼 SQL维护成本高不少。1.3 Node.js 在这里到底起什么作用很多人看到“nodejs 基于 django”会疑惑这俩不都是后端吗怎么放一起了我在这个项目里实际是把 Node.js 定位成“小程序前端工程化和辅助脚本工具链”而不是第二个后端服务。具体做的事情包括三块用 npm 管理小程序项目用到的第三方库和构建工具比如 miniprogram-ci它可以命令行上传小程序代码方便接入 CI/CD。写 Node.js 脚本做模拟数据生成、批量修改文件、调用接口测试比如生成几百条设备数据塞进 Django 后端。如果未来要给用户发订阅消息或者做消息推送也可以单独用 Node.js 写一个推送服务通过 HTTP 接口和 Django 配合。不过核心业务逻辑还是 Django 负责Node.js 不抢这个活。这个定位很重要它决定了你搭环境时两条腿都要走一条是 Python/Django 的开发环境另一条是 Node.js/npm 的前端工具环境。下面我先把环境这部分讲透因为热搜词里大量出现的 npm.ps1、Node.js 安装问题多半都卡在这一步。2. 环境准备先把开发用的两台引擎装好2.1 Python 与 Django 环境配置先确认你电脑里有 Python 3.8 以上版本。装完以后建议不要全局装 Django而是给每个项目单独建虚拟环境这样不会污染系统环境。# 创建项目目录 mkdir device-repair cd device-repair # 创建并激活虚拟环境 python -m venv venv # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate # 安装 Django 和 DRF、跨域组件 pip install django djangorestframework django-cors-headers # 创建项目和 app django-admin startproject config . python manage.py startapp repair这里有几个新手容易懵的点startproject config .后面的点和空格不能省表示在当前目录生成项目配置文件。app 名称我用的是repair和项目名区分开。创建 app 之后记得去config/settings.py的 INSTALLED_APPS 里注册repair以及rest_framework、corsheaders不注册的话后面 migrate 会莫名其妙失败。跨域组件在前后端分离场景下一定要装。小程序本身没有浏览器同源策略那么严格但如果你后面用网页版调试跨域就躲不开。corsheaders的配置很简单INSTALLED_APPS [ # ... corsheaders, rest_framework, repair, ] MIDDLEWARE [ corsheaders.middleware.CorsMiddleware, # ... ] CORS_ALLOW_ALL_ORIGINS True # 开发阶段可以先全开开发阶段全开方便调试上线前一定收紧不然别人随便拿你的接口去刷数据。2.2 Node.js 与 npm 安装以及 npm.ps1 报错Node.js 的安装包直接去官网下载 LTS 版本Windows 安装时一路 Next 就好。装完以后打开终端验证node -v npm -v这里我要专门说一下那个烦人的报错npm : 无法加载文件 C:\Program Files\nodejs\npm.ps1因为在此系统上禁止运行脚本原因很简单Windows PowerShell 默认执行策略是 Restricted不允许运行本地脚本文件。npm.ps1 本质上就是个脚本所以被拦了。解决方法有两种第一种在 PowerShell 里临时放开当前用户的执行策略Set-ExecutionPolicy -Scope CurrentUser RemoteSigned输入 Y 确认就完事。RemoteSigned 的意思是本地脚本可以运行。从网上下载的脚本必须带数字签名才能运行。这个安全策略比以前宽松但也没有完全裸奔日常开发够用。第二种如果你不想改策略可以直接用 npm.cmd 代替npm.cmd -v不过那样每次都要多敲几个字母很麻烦。我更推荐直接改执行策略一劳永逸。Node.js 的另一个常见问题是 npm 官方源在国内很慢。这个不会报错但安装依赖时能让你等到怀疑人生。可以用镜像源解决npm config set registry https://registry.npmmirror.com改完之后可以npm config get registry确认。注意只要是给你的 Node.js 工具装依赖这个步骤基本都适用。2.3 微信开发者工具与小程序的工程结构微信小程序本身不需要安装额外的编译器官方“微信开发者工具”就够。装好后用小程序管理员账号扫码登录新建项目时选“不使用云服务”填入自己的 AppID没有 AppID 的可以先选“测试号”。这时的工程结构是 Vite 那套吗不是微信原生小程序没有 Vite它就是一个包含pages/、app.js、app.json、project.config.json的目录。你可以直接手动创建目录也可以用小程序开发工具新建一个空白模板。后面要引入 npm 包比如 miniprogram-ci开发者工具还需要开启“使用 npm 模块”这个选项并且在项目根目录执行npm init -y生成 package.json。这一步就是 Node.js 真正参与进来的入口。3. Django 后端核心实现3.1 数据模型设计先想清楚表和关系设备报修管理系统虽然业务简单表结构却不该含糊。我在设计时一共建了五张核心表用户扩展表、设备分类表、设备表、报修工单表、工单日志表。先看用户表。Django 自带的 User 能满足登录用但我们要存角色、手机号、微信 openid所以要做一个 AbstractUser 子类from django.contrib.auth.models import AbstractUser from django.db import models class User(AbstractUser): ROLE_CHOICES ( (user, 普通用户), (repairer, 维修员), (admin, 管理员), ) role models.CharField(max_length20, choicesROLE_CHOICES, defaultuser) phone models.CharField(max_length11, blankTrue) openid models.CharField(max_length64, blankTrue, uniqueTrue) class Meta: db_table auth_user这里把 openid 设成唯一因为一个小程序用户对应一个 openid绑定了就不会重复注册。注意如果你要兼容小程序登录和账号密码登录两种情况openid 字段一定要允许为空否则普通后台账号就建不出来了。设备分类和设备表是嵌套关系。设备表里至少有设备编码、名称、分类外键、安装位置、状态class DeviceCategory(models.Model): name models.CharField(max_length50) desc models.TextField(blankTrue) class Device(models.Model): code models.CharField(max_length50, uniqueTrue) name models.CharField(max_length100) category models.ForeignKey(DeviceCategory, on_deletemodels.PROTECT) location models.CharField(max_length200) status models.CharField( max_length20, choices((normal,正常),(repairing,维修中),(broken,故障)), defaultnormal ) created_at models.DateTimeField(auto_now_addTrue)注意设备分类外键用on_deletemodels.PROTECT意思是如果这个分类下还有设备就不允许删分类防止把设备表的数据搞成“无所属”。这是很多新手容易忽略的细节。报修工单表则要关联设备、报修人、维修员同时记录工单号、问题描述、图片列表、状态、创建时间、更新时间class RepairOrder(models.Model): STATUS_CHOICES ( (pending, 待派单), (assigned, 已接单), (repairing, 维修中), (completed, 已完成), (rejected, 已驳回), ) order_no models.CharField(max_length32, uniqueTrue) device models.ForeignKey(Device, on_deletemodels.CASCADE) reporter models.ForeignKey(User, on_deletemodels.PROTECT, related_namereported_orders) repairer models.ForeignKey(User, on_deletemodels.SET_NULL, nullTrue, blankTrue, related_nameassigned_orders) description models.TextField() images models.JSONField(defaultlist) status models.CharField(max_length20, choicesSTATUS_CHOICES, defaultpending) created_at models.DateTimeField(auto_now_addTrue) updated_at models.DateTimeField(auto_nowTrue)images用 JSONField 存图片地址列表比单独建一张图片表简单报表和前端渲染都方便。如果以后图片量非常大再拆表也来得及初期没必要过度设计。工单日志表我建议一定要加用来记录每一次状态变更、操作人和操作时间后续吵架调证据全靠它。3.2 API 接口清单前后端数据是怎么对上的我把接口按角色权限划分了一下实际项目可以直接照着这个清单去实现接口路径方法功能权限/api/login/POST小程序 code 换 token匿名/api/devices/GET拉取设备列表登录用户/api/orders/POST提交报修单登录用户/api/orders/my/GET查询我提交的报修登录用户/api/orders/assigned/GET维修员查自己被派工单维修员/管理员/api/orders/{id}/assign/POST管理员派单管理员/api/orders/{id}/status/PATCH维修员更新状态维修员/管理员/api/orders/{id}/reject/POST管理员驳回工单管理员用 DRF 写接口时序列化器长这样from rest_framework import serializers from .models import RepairOrder class RepairOrderSerializer(serializers.ModelSerializer): device_name serializers.CharField(sourcedevice.name, read_onlyTrue) reporter_name serializers.CharField(sourcereporter.username, read_onlyTrue) class Meta: model RepairOrder fields [id, order_no, device, device_name, reporter_name, description, images, status, created_at] read_only_fields [order_no, status, created_at]这里要注意前端提交报修单时只需要传device、description、images三个字段其他的像order_no、status应该由后端生成。所以序列化器里把 device 和 description 设为可写把 order_no 和 status 标成 read_only就杜绝了用户伪装管理员改状态的可能。3.3 核心业务逻辑与权限控制接口文档只是骨架真正的灵魂在视图层里的业务判断。报修系统最容易出问题的地方是三处第一处报修单号生成。不要在数据库里直接拿时间戳拼并发会撞车。我用的是import uuid order_no WO uuid.uuid4().hex[:12].upper()这样每条工单基本不可能重复而且看起来还挺像那么回事。第二处状态机流转控制。比如只有“待派单”状态允许管理员派单只有“已接单”和“维修中”状态允许维修员更新进度。不能给一个方法无限改状态。我写了一个简单的检查from rest_framework.exceptions import ValidationError ALLOWED_TRANSITIONS { pending: [assigned, rejected], assigned: [repairing], repairing: [completed], } def validate_transition(order, new_status): if new_status not in ALLOWED_TRANSITIONS.get(order.status, []): raise ValidationError(f当前状态不能变更为 {new_status})这看起来简单却避免了一个常见 bug管理员已经派单维修员却没有接单结果直接点了“已完成”。流程被跳过数据就假了。第三处权限控制。在 DRF 视图里可以直接用权限类。不过这个小项目我更习惯在视图中判断角色if request.user.role ! admin: return JsonResponse({error: 无权限}, status403)如果你的前后端分工明确也可以在权限类里统一做。怎么实现都能跑但权限逻辑必须集中管理千万别一个视图一种写法后面容易漏。搜索热词里有一个“django执行查询-删除对象”这个其实很实用。比如管理员想清理很久以前的已完成工单RepairOrder.objects.filter(statuscompleted, updated_at__lt2024-01-01).delete()一条语句就能批量删除。但重点提醒批量删除不会触发模型里重写的delete()方法也不会自动处理关联信号。如果想删之前备份一定要先查出来写进日志再执行删除。我的习惯是orders RepairOrder.objects.filter(statuscompleted, updated_at__ltcutoff) for o in orders: print(f删除工单{o.order_no}) orders.delete()先打印再删至少留个操作痕迹。3.4 图片上传小程序拍照后怎么存报修单没有图描述经常说不清。所以图片上传必须做。Django 处理图片上传需要配置 MEDIA_ROOT 和 MEDIA_URLMEDIA_URL /media/ MEDIA_ROOT BASE_DIR / media主路由里再挂上静态文件服务Django 开发环境可以直接用static()处理 media 路径线上交给 Nginx。配置好之后接口接收前端上传的图片时用 DRF 的 FileField 或者直接读request.FILESfrom django.conf import settings import os def upload_image(request): file request.FILES.get(file) if not file: return JsonResponse({error: no file}, status400) path os.path.join(settings.MEDIA_ROOT, file.name) with open(path, wb) as f: for chunk in file.chunks(): f.write(chunk) return JsonResponse({url: request.build_absolute_uri(settings.MEDIA_URL file.name)})说一个实际坑小程序端上传图片用的临时文件路径是wxfile://开头直接存到 Django 里不可用。必须先上传拿到服务器返回的 URL再把 URL 放到报修单的 images 字段里。这个链路很多人第一次做都会掉进去。4. 微信小程序前端实现4.1 页面结构与顶部导航适配小程序的页面我分成四块首页设备列表 报修入口、报修单提交页、我的工单列表、工单详情页。底部 Tab 两个设备报修、我的工单。管理员和维修员的页面则是复用同一个工单列表根据角色显示不同的操作按钮。顶部导航栏高度是个很容易踩坑的点。小程序默认导航栏在不同机型上高度不一样尤其 iPhone 的刘海屏和普通安卓机差距很大。如果你用的是默认导航栏不用管高度但如果你想自定义顶部导航比如放一个大标题和搜索框就要拿 API 动态算高度// app.js 或者页面 onLoad 里 const { statusBarHeight } wx.getSystemInfoSync() const menuButton wx.getMenuButtonBoundingClientRect() const navHeight (menuButton.top - statusBarHeight) * 2 menuButton.height这里的navHeight就是自定义导航栏需要占的高度。原理是胶囊按钮底部到状态栏底部的距离的两倍加上按钮本身高度正好等于导航栏高度。这段代码在多端适配时非常关键否则标题栏在 iPhone 上会被刘海挡住。4.2 报修列表页与上拉加载更多工单列表不能一次把几千条数据全塞给小程序的 setData性能扛不住。标准做法是分页加载页面最下面加上“加载更多”或者“已经到底了”。核心变量就四个data: { list: [], page: 1, pageSize: 10, hasMore: true, loading: false }页面滚动到底部会触发onReachBottom这时候去拉下一页async loadOrders() { if (!this.data.hasMore || this.data.loading) return this.setData({ loading: true }) const res await request.get(/api/orders/my/, { page: this.data.page, page_size: this.data.pageSize }) this.setData({ list: this.data.list.concat(res.data.results), page: this.data.page 1, hasMore: res.data.next ! null, loading: false }) }loading这个状态很重要它防止用户狂滑屏幕时连续触发十几个请求。后端的 page_size 如果设为 10返回里带一个next字段前端根据 next 是否为 null 判断还有没有下一页。这个拉数据的模式我建议直接写成通用封装后面所有列表页都能复用。4.3 报修表单页与单选框组件提交报修单的页面里设备选择可以用 picker故障类型用 radio 单选。微信小程序原生单选框是radio-group包radioradio-group bindchangeonFaultChange label wx:for{{faultTypes}} wx:key*this radio value{{item.value}} checked{{item.checked}} / text{{item.label}}/text /label /radio-group对应的faultTypes数据放几个常见类型比如硬件损坏、软件问题、网络故障、其他。选择后的 value 存到 form 对象里。这里有个小技巧单选框的样式很丑为了和页面风格一致我一般把 radio 藏起来自己写一个自定义选中样式。用label包住按钮和文字点击区域变大体验会好很多。图片上传那块直接用wx.chooseMediawx.chooseMedia({ count: 3, mediaType: [image], sizeType: [compressed], success(res) { const file res.tempFiles[0].tempFilePath wx.uploadFile({ url: https://api.example.com/api/upload/, filePath: file, name: file, success(uploadRes) { const url JSON.parse(uploadRes.data).url // 把 url 追加到 images 数组 } }) } })压缩图能省不少流量报修系统拍设备故障照片用 compressed 完全够用。一次性最多选 3 张防止用户疯狂传原图把服务器磁盘打爆。4.4 用户登录与状态管理小程序端登录流程不是你想的那样“用户名密码登录”而是用微信的静默授权wx.login拿到一个临时 code传给后端后端拿 code 去微信接口换 openid 和 session_key再生成自己的 token 返回给前端。wx.login({ success(loginRes) { wx.request({ url: https://api.example.com/api/login/, method: POST, data: { code: loginRes.code }, success(res) { const { token, user } res.data wx.setStorageSync(token, token) wx.setStorageSync(user, user) } }) } })后端收到 code 后用 Django 的requests库调微信接口import requests def wx_code_to_openid(code): appid 你的AppID secret 你的AppSecret url fhttps://api.weixin.qq.com/sns/jscode2session?appid{appid}secret{secret}js_code{code}grant_typeauthorization_code resp requests.get(url).json() return resp.get(openid)拿到 openid 后在 User 表里查或建用户然后把一个随机 token 返回给小程序。后面每个请求 header 里带上Authorization: Token xxx后端识别当前用户身份。这套流程我推荐用 DRF 的 TokenAuthentication不需要额外引入 JWT简单很多。5. 前后端联调与数据流转5.1 从“提交报修”到“生成工单”的完整链路把前端和后端串起来最核心的一条链路是用户选择设备 → 填写故障描述 → 点击提交 → 小程序请求/api/orders/→ Django 校验权限、生成工单号、存库 → 返回工单信息 → 小程序跳转工单详情页。这条链路里最容易出的问题有两个。第一个是请求封装。小程序不能像浏览器那样直接 fetch 就完事每个请求都要带 token还要统一处理 401。所以我习惯封装一个request模块function request(url, method, data) { return new Promise((resolve, reject) { const token wx.getStorageSync(token) wx.request({ url: https://api.example.com${url}, method: method || GET, data: data || {}, header: { Authorization: token ? Token ${token} : , Content-Type: application/json }, success(res) { if (res.statusCode 401) { // 重新登录 wx.navigateTo({ url: /pages/login/login }) reject(res) } else { resolve(res) } }, fail: reject }) }) }第二个问题是图片上传后返回的 URL 必须是完整地址。如果后端返回/media/xxx.jpg小程序wx.previewImage只能预览本地文件没法直接预览线上图片它会认为这是一个不存在的相对路径。所以上传接口要返回http://域名/media/xxx.jpg这种完整格式或者前端自己拼接域名。这里我用request.build_absolute_uri来解决。5.2 调试技巧开发时怎么抓包看小程序请求小程序开发工具自带的 Network 面板够用但真机调试时有些返回数据看不到或者你想看整个请求头、响应体、状态码这时候可以用 Charles 做代理抓包。我平时最常用的方式在 Mac/Windows 上打开 Charles开启 SSL Proxying把手机代理指向电脑 IP 和 Charles 端口然后手机上打开微信小程序所有请求都会出现在 Charles 列表里。你可以看到请求的 URL 是否走对了环境测试服/正式服。请求头里的 Authorization 有没有带上。响应体里的 JSON 字段名是否和前端代码一致。哪一步请求拖慢了加载速度。有一次我排查“工单列表一直加载不出来”的问题用 Charles 一看发现接口返回的是results数组但前端代码里取的是res.data.data字段对不上页面当然是空的。这种问题如果不抓包光看代码得排查很久。需要注意抓包工具只是调试手段不要用来做任何违规操作。日常开发调试完全没问题上线前记得关闭相关的代理设置。5.3 部署上线要点Nginx uWSGI 域名校验小程序正式上线要求接口必须走 HTTPS且域名必须在小程序后台配置为合法域名。这是很多第一次发布小程序的人最难受的一关。后端部署我用的方案是云服务器 Nginx uWSGI 跑 Django。大概步骤服务器上创建虚拟环境安装项目依赖。收集静态文件python manage.py collectstatic。用 uWSGI 启动 Djangouwsgi --http :8000 --module config.wsgi配置文件里选socket模式。Nginx 配置反向代理把/api/转到 uWSGI同时处理/media/静态文件。申请 HTTPS 证书并绑定域名。在小程序后台把https://api.example.com加到 request 合法域名和 uploadFile 合法域名。配置里有一个坑如果 Django 的ALLOWED_HOSTS没加服务器域名或 IP访问接口会报 400。我在开发环境写的是ALLOWED_HOSTS [*]上线前改成了具体域名顺便把DEBUG False打开。注意上线后DEBUG False如果不配好静态文件托管admin 后台样式会全丢Nginx 里location /static/ { alias /path/to/static/; }这行一定不能少。小程序发布流程相对简单开发者工具点“上传”填版本号和备注到小程序管理后台提交审核审核通过后发布。个人开发者不用付费但如果要开通微信支付或者某些特殊接口才涉及认证费用设备报修系统一般不需要这些所以成本主要在服务器。6. 落地过程中踩过的坑和解决笔记6.1 常见问题速查表我把这个项目里遇到过的问题整理成一张表按出现频率排序问题现象根本原因解决办法npm 命令无法执行提示禁止运行脚本PowerShell 执行策略限制Set-ExecutionPolicy -Scope CurrentUser RemoteSigned小程序请求后端报“不在合法域名列表”正式环境域名未配置在小程序后台添加 request/uploadFile 合法域名开发工具里请求正常真机请求失败真机没开“不校验合法域名”或根本没配域名开发阶段用工具勾选“不校验合法域名”真机调试用测试版Django 返回中文乱码终端编码问题或数据库连接未指定 utf8mb4Shell 里chcp 65001数据库连接参数字典加 charset图片上传后小程序无法显示后端返回了相对路径使用完整绝对 URL工单列表没有数据但后端有数据前端过滤条件或分页参数不对抓包看请求参数和响应结构用户反复点击提交按钮导致重复工单没有防重复提交提交后立刻禁用按钮或后端做幂等校验接口报 403当前用户角色没有权限检查权限判断是管理员/维修员角色再放行数据库删除外键关联数据报错on_delete 策略选择不对按业务场景换成 PROTECT、CASCADE 或 SET_NULL这里我单独说说“数据库删数据被外键拦住”的问题。比如你想删一个普通用户但他已经提交过报修工单那reporter外键会保护住禁止你删。这其实是好事防止历史数据失真。如果真想删先归档工单再在工单里把外键改成 SET_NULL或者不做物理删除只给用户加一个 is_activeFalse 的禁用标记。我强烈推荐后者用户禁用比删除更安全。6.2 我从这个项目里学到的经验第一状态机一定要设计在数据模型层面而不是只在前端按钮上控制。曾经有人把“已接单”状态只写了前端变量结果两个维修员同时点接单后端完全没有兜底工单被抢成脏数据。后来我把状态变更收敛到后端接口里前端只是发一个“期望操作”具体能不能变后端说了算。第二权限判断要尽早做别等业务逻辑写完再补。如果一开始就定好“普通用户只能操作自己创建的工单维修员只能看被分派的工单管理员可以派单和驳回”接口代码会好写很多也不会出现用户把别人的工单改成已完成这种事故。第三小程序端和 Django 端的字段命名尽量一致。比如前端传description后端模型字段也叫description前端传device传设备 id后端序列化器就处理成device。千万别一会儿用下划线一会儿用驼峰前后端联调时全在改字段名上浪费时间。第四Node.js 工具链虽然不参与业务逻辑但千万别忽视。我遇到过npm install后 miniprogram-ci 版本太老导致上传失败也遇到过 npm 包锁版本不严格导致本地构建通过、CI 构建失败。如果你在小程序项目里用了任何 npm 包提交代码时一定要把 package-lock.json 一起提交所有环境才能装出同样的依赖。最后再分享一个小技巧开发阶段在 Django 的local_settings.py里把DEBUGTrue、ALLOWED_HOSTS[*]写死但生产环境的settings.py走独立配置。用环境变量控制环境切换别每次上线都手改代码。我这个项目一开始就是因为直接改了 settings.py 又忘记改回来测试服和正式服反复同步浪费了不少时间。用环境变量之后这个问题再也没出现过。