1. 项目背景与整体设计思路
每年八月底到九月初,高校的新生报到场景几乎都是同一个画面:体育馆里临时搭起几十张桌子,学生和家长拖着行李箱挤在各个学院摊位前排队,填表、核对证件、交材料、领一卡通,一个流程跑下来少说一两个小时。如果是几千人的规模,那整个报到大厅基本就是个人工调度现场。
我这次做的这个项目,就是把这个线下流程搬到线上。
系统拆成两端:学生端用微信小程序,负责新生预报到注册——录取信息确认、基础资料填写、到校时间登记、生成个人报到二维码;管理端则由学校、学院的老师在后台或小程序里扫码核验,确认学生到校后完成正式报到。后端整体基于 Django + Python 开发,对外提供 JSON API,前端小程序只负责渲染和交互,数据全部走后端接口。
这套方案直接解决了三个核心问题:
- 报到数据电子化,告别纸质表,后续统计宿舍分配、军训服尺码、各学院报到率都有实时数据。
- 报到流程前置,学生在家就能完成预报到,现场只需要扫码确认,单个学生的核验时间从十几分钟压缩到十几秒。
- 学校各学院的管理员可以实时看到本院的报到进度,不需要反复手动汇总Excel。
技术选型上,后端用 Django 而不是 Flask,是因为这个系统天然带有较复杂的业务模型——学生、录取信息、院系专业、报到记录、用户认证,Django 的 ORM、Admin 后台、自带认证体系能省掉大量重复劳动。前端选微信小程序则是最实际的选择,微信用户基数大,不需要额外安装 App,扫一扫即可进入,几乎零学习成本。这个项目的定位适合计算机相关专业的学生作为毕业设计,也适合高校信息化团队参考业务流程,或者技术开发者从零搭建一个完整的微信小程序 + Django 前后端分离项目。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据模型设计——把报到业务拆成表
2.1 核心模型与业务规则
整个系统的数据模型是整个项目的命脉。业务跑得顺不顺,字段设计合理不合理,直接决定后续开发的复杂度。我设计的时候主要围绕五个核心模型展开。
第一个是用户模型 User,我直接继承 Django 自带的 AbstractUser 做扩展。微信小程序登录拿到的 openid 是用户的唯一标识,所以要在 User 上增加 openid、unionid、avatar、nickname 这些字段。这里有个经验,不要另建一张用户信息表跟 User 一对一关联,直接扩展 AbstractUser 最省事,Django 的认证体系、Admin 后台、session 机制全部保留,开发效率高得多。
第二个是学生档案模型 StudentProfile,字段包括学号、姓名、性别、身份证号、手机号、出生日期、政治面貌、生源地、家庭住址、紧急联系人等。学号的设计有个讲究,一般可以做成“入学年份 + 学院代码 + 专业代码 + 顺序号”,比如 20250101001,这样学号本身就携带了学院和专业信息,查询和识别都方便。身份证号需要做唯一约束,后期学校系统对接的时候身份证号往往是关联键。
第三个是录取信息模型 EnrollmentInfo,字段包括考生号、录取通知书编号、录取学院、录取专业、报到校区、学费标准、是否保留入学资格等。值得注意的是,录取信息应该是报到系统的基础数据,学生端在预报到的时候只能查看和确认,不能修改录取学院和专业,这样避免学生误操作改动核心数据。如果有修正需求,应该由管理员在后台操作。
第四个是报到记录模型 ReportRecord,这是整个系统使用频率最高的表。每次学生在小程序端提交预报到,或者管理员扫码确认正式报到,都会写入一条记录。字段包括 student、type(预报道/正式报到)、status(待审核/已通过/已驳回)、提交时间、报到时间、报到地点、操作人、备注等。我特意加了一个 type 字段来区分预报到和正式报到,因为这两个时间点可能相差很远,一个学生在八月完成了预报到,九月才到校现场完成正式报到,不能混在一条记录里。
第五个是动态配置模型,包括学院表 College、专业表 Major、报到批次表 ReportBatch。学校每年有不同批次的新生,比如普通本科、专升本、研究生,不同批次的报到时间和地点不一样,所以用 ReportBatch 来管理报到时间窗,每个批次关联自己的开始时间和结束时间。这样学生登录后,系统根据他的录取批次自动判断当前是否在可预报到的时间范围内。
2.2 报到码的生成策略
报到码是整个线下核销环节的关键。我采用“短码 + 二维码”双层方案。短码是6位数字或字母组合,例如 8A3K2M,方便人工备用核验;二维码则是对短码进行编码后用前端库渲染生成。
短码的生成不能简单地用自增ID,容易暴露报到总人数,而且便于猜测,安全性差。我的做法是用 hashids 这类库把数据库ID编码成不可预测的短字符串,或者在生成时存一个 UUID 短片段,入库前做好唯一性校验。实际项目里我用的是独立字段 report_code,带唯一索引,生成算法是取 UUID 的前6位,转成大写后查重,有冲突就重新生成,冲突概率极低。
注意:报到码一定要做到不可枚举。如果学生报到码是连续的纯数字,那别人随便改一下就知道了你的报到状态,属于越权访问漏洞。
2.3 表结构设计的几个坑
这里说几个实际开发中很容易踩的坑。
第一个坑是时间字段的时区问题。Django 默认开启 USE_TZ,写入数据库的时间是 UTC 时间,如果前端小程序直接展示接口返回的时间字符串,就会比北京时间慢8小时。我个人的处理是:在 model 里统一用 DateTimeField,序列化输出时用 Django 的 localtime 转成本地时间,或者干脆在 setting 里设置 TIME_ZONE = 'Asia/Shanghai' 且 USE_TZ = False,简单场景下省心不少。
第二个坑是身份证号码的存储类型。身份证号有18位,用 CharField(max_length=18) 没问题,但要注意末尾可能是X,不要用 IntegerField。手机号同理,不要用数字类型,因为数字类型会丢掉前导零——虽然手机号目前没有前导零,但这是习惯问题。
第三个坑是性别、政治面貌这类固定选项字段,用 Django 的 TextChoices 定义枚举,数据库存字符串,避免散落魔数。例如:
python复制class Gender(models.TextChoices):
MALE = 'M', '男'
FEMALE = 'F', '女'
UNKNOWN = 'U', '未填写'
这样代码可读性高,后续做筛选统计也方便。
第四个坑是新生照片、身份证照片这类文件字段。用 ImageField 没问题,但必须注意 MEDIA_ROOT 和 MEDIA_URL 的配置,以及上线后 Nginx 对 media 目录的访问权限。很多人在本地跑得好好的,部署到服务器发现图片无法访问或无法上传,基本都是静态文件和媒体文件路径没配置对。
3. 后端 API 设计与实现
3.1 接口清单与路由规划
整个后端我用 Django REST Framework(DRF)来组织接口。用 DRF 的理由很简单,它提供的 ModelViewSet、序列化器、权限控制、分页、过滤这些功能,几乎覆盖了报到系统80%的接口需求,写起来比手工写 JSONResponse 高效得多。
整体接口规划如下:
| 模块 | 接口路径 | 请求方式 | 功能说明 |
|---|---|---|---|
| 认证 | /api/auth/login | POST | 微信登录,用 code 换 token |
| 认证 | /api/auth/profile | GET/PUT | 获取/更新当前用户资料 |
| 学生 | /api/student/enrollment | GET | 获取本人录取信息 |
| 学生 | /api/student/profile | GET/PUT | 获取/更新预报到资料 |
| 报到 | /api/report/pre-submit | POST | 提交预报到 |
| 报到 | /api/report/status | GET | 查询报到状态 |
| 报到 | /api/report/qrcode | GET | 获取本人报到码信息 |
| 管理员 | /api/admin/scan | POST | 扫码核验,确认正式报到 |
| 管理员 | /api/admin/stats | GET | 报到统计看板数据 |
权限控制上,用 DRF 的 IsAuthenticated 作为全局默认权限,然后在 Admin 相关接口上叠加 IsAdminUser 或自定义权限。学生接口和管理员接口必须严格隔离,避免学生通过修改请求参数访问管理端数据。
3.2 微信登录与用户绑定
微信小程序登录的流程是:小程序端调用 wx.login 获取临时 code,把 code 发送到后端,后端拿着 code 去微信接口换取 openid 和 session_key,然后后端自己签发一个 token 返回给小程序。之后小程序每次请求都带上这个 token,不再依赖微信的 session_key。
这里有一个很关键的点:为什么要自己签发 token,而不是直接拿微信的 session_key 当凭证?因为 session_key 是微信服务器管理的,我们无法控制它的过期时间,而且它不应该暴露给客户端。正确的做法是换到 openid 后,查询或创建本地用户,然后签发自己的 token。推荐用 JWT 或 DRF 自带的 TokenAuthentication。我用的是 django-rest-framework-simplejwt,因为它支持过期时间配置,安全性和易用性都比较好。
python复制# 登录视图核心逻辑(简要)
import requests
import random
import string
from django.conf import settings
from django.contrib.auth import get_user_model
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
User = get_user_model()
class WeChatLoginView(APIView):
permission_classes = []
authentication_classes = []
def post(self, request):
code = request.data.get('code')
if not code:
return Response({'error': '缺少code参数'}, status=status.HTTP_400_BAD_REQUEST)
# 小程序端需要将 appid 和 secret 配置在后端环境变量,不要放在前端代码里
url = 'https://api.weixin.qq.com/sns/jscode2session'
params = {
'appid': settings.WECHAT_APPID,
'secret': settings.WECHAT_SECRET,
'js_code': code,
'grant_type': 'authorization_code',
}
resp = requests.get(url, params=params, timeout=5).json()
openid = resp.get('openid')
if not openid:
return Response({'error': '登录失败,请重试'}, status=status.HTTP_401_UNAUTHORIZED)
user, created = User.objects.get_or_create(
username=openid,
defaults={'openid': openid}
)
refresh = RefreshToken.for_user(user)
return Response({
'token': str(refresh.access_token),
'is_new_user': created,
'user': {'id': user.id, 'nickname': user.nickname}
})
这里建议把 appid 和 secret 放在 Django 的 environment 配置或本地配置文件中,绝对不要写在前端小程序代码里,否则任何人反编译小程序都能拿到你的密钥,后果很严重。
还有个细节:get_or_create 用 username=openid 做账号,是因为 Django 的 User 模型默认要求 username 唯一,直接用 openid 当 username 可以省掉额外字段的唯一性处理。如果你要扩展 openid 字段,需要给它加 unique=True 并在创建时判重。
3.3 预报到提交与状态机流转
预报到这个动作不是一个简单的 INSERT 语句就行,里面有一整套状态校验逻辑。我把报到状态设计成:
code复制NOT_SUBMITTED(未提交)→ PENDING(待审核)→ APPROVED(已通过)
↘ REJECTED(已驳回)→ 可重新提交
学生提交预报到时,后端要做以下几件事:
- 验证当前时间是否在报到批次的时间窗内。
- 验证学生是否已经提交过预报到,如果提交过且状态是待审核,不允许重复提交。
- 校验必填字段是否完整,比如手机号格式、身份证号格式、紧急联系人是否填写。
- 写入首次预报到记录,状态置为待审核。
- 管理员审核通过后,系统生成正式报到码,并把二维码内容通过模板消息(或公众号订阅消息)推送到学生微信。
这个流程的设计重点在于:为什么不让学生提交后就立即生成报到码?因为预报到的资料是需要管理员审核的,比如学生填写的到校时间是否合理、证件照是否清晰。如果资料没审核就发放报到码,现场就会出现信息错误纠纷。加一道人工审核,能有效提高数据质量。
管理员审核接口的逻辑也很简单,核心是状态更新和备注记录:
python复制class AdminReviewReportView(APIView):
permission_classes = [IsAdminUser]
def post(self, request, record_id):
record = ReportRecord.objects.select_related('student').get(id=record_id)
action = request.data.get('action') # approve / reject
remark = request.data.get('remark', '')
if action == 'approve':
record.status = ReportRecord.Status.APPROVED
record.review_remark = remark or '审核通过'
# 生成报到码
record.report_code = self.generate_report_code()
record.save()
elif action == 'reject':
record.status = ReportRecord.Status.REJECTED
record.review_remark = remark or '资料不完整,请修改后重新提交'
record.save()
return Response({'message': '操作成功'})
3.4 扫码核验接口的实现要点
管理员端扫码核验是整个系统的收官动作。管理员打开小程序的管理页面,点击“扫一扫”,调用 wx.scanCode 扫学生的报到二维码,拿到码内容后调用后端的 /api/admin/scan 接口。
后端接口要做的事包括:
- 根据 report_code 查询报到记录。
- 校验报到记录的状态是否为已通过,否则返回“该学生尚未完成预报到”。
- 校验当前时间是否在正式报到时间窗口内。
- 校验该学生是否已经完成过正式报到,避免重复扫码。
- 在校验通过后,更新正式报到时间,写入报到地点和操作管理员。
这个接口有几个防并发和防重复的关键点。第一,ReportRecord 表需要加一个 unique 约束或使用 select_for_update 进行行锁,防止两个管理员同时扫同一个码导致重复报到。第二,正式报到动作的 status 变更需要加条件更新,例如 UPDATE ... SET status='REPORTED' WHERE status='APPROVED' AND id=...,通过受影响行数判断是否重复操作。
我用了一段比较稳妥的条件更新逻辑:
python复制from django.db.models import F
updated = ReportRecord.objects.filter(
id=record.id,
status=ReportRecord.Status.APPROVED
).update(
status=ReportRecord.Status.REPORTED,
report_time=timezone.now(),
report_location=location,
operator=request.user,
)
if updated == 0:
return Response({'error': '该报到码已被使用或状态异常'}, status=400)
4. 微信小程序前端实现
4.1 项目建设基础配置
小程序端整体页面不算复杂,但有几个基础配置必须先行处理好。
第一是服务器域名配置。微信小程序要求所有请求的 URL 必须是 HTTPS 且在小程序后台配置了合法域名。开发调试阶段可以在开发者工具右上角“详情 -> 本地设置”勾选“不校验合法域名、web-view(业务域名)、TLS 版本以及 HTTPS 证书”,但上线前一定要配置正式域名,否则真机测试会直接报 net::ERR_CERT_INVALID 或 url not in domain list。
第二是项目结构。我习惯按业务模块划分:
code复制pages/
index/ # 首页:报到状态入口
login/ # 登录页
student/ # 学生资料填写
enrollment/ # 录取信息确认
pre_report/ # 预报到表单页
qrcode/ # 报到码展示页
admin/ # 管理员扫码页
admin_stats/ # 报到统计页面
utils/
request.js # 封装 wx.request,自动带 token
auth.js # 登录态管理
第三是公共请求封装。每个页面都直接调 wx.request 会非常痛苦,既要处理 token 过期,又要处理错误码统一弹窗。我在 utils/request.js 里封装了一个 request 方法,基于 Promise,自动在 header 里携带 token,遇到 401 自动重新登录再重放请求。
4.2 登录与用户身份识别
小程序端登录入口通常放在首页 onLoad 时触发。核心代码如下:
javascript复制// utils/auth.js
function login() {
return new Promise((resolve, reject) => {
wx.login({
success(res) {
if (res.code) {
wx.request({
url: `${BASE_URL}/api/auth/login/`,
method: 'POST',
data: { code: res.code },
success: (resp) => {
const { token, user } = resp.data;
wx.setStorageSync('token', token);
wx.setStorageSync('user', user);
resolve(resp.data);
},
fail: reject
});
} else {
reject(new Error('wx.login failed'));
}
},
fail: reject
});
});
}
这里有一个很容易忽略的细节:wx.login 返回的 code 有效期只有5分钟,且只能使用一次。如果后端接口超时或报错,重新调用 wx.login 获取新 code 再发起请求,不要拿原来的 code 重试。
另外,这种静默登录只能拿到 openid,拿不到用户的手机号和微信昵称。如果产品上需要展示昵称和头像,可以引导用户授权,但注意现在微信已经收紧了很多授权接口。我在这个项目里没做强制授权,因为新生报到系统最重要的是手机号和学号信息,这些通过表单录入更可靠,不要依赖微信的用户信息。
4.3 预报道表单的关键组件
预报到表单是学生端最核心的页面。字段包括:手机号、紧急联系人、家庭住址、到校日期、到校时间段、军训服尺码、是否自驾、随行人数等。
这里有几个组件层面的实践要点。
手机号输入框要用 type="number" 调起数字键盘,但注意最大长度设为11位,而且微信原生 input 的 type 不支持直接在手机上弹数字键盘时保留分隔符,所以要自己过滤非数字字符。
省市区三级联动用 picker-view 实现,比 picker 的 mode="region" 更灵活。不过微信官方已经提供了 picker mode="region",基础库2.9.0以上支持,可以直接用,省事不少。但如果学校有更细的街道级信息需求,就得自己维护行政区划 JSON 了。
日期选择用 picker mode="date",设定 start 和 end 范围,学生只能选择报到批次内的日期,这在前端先做一层约束,后端也要再做一次校验,不能只依赖前端。
军训服尺码用 radio-group 渲染,选项为 S/M/L/XL/XXL/XXXL,并在选项旁标注身高参考范围,方便学生选择。
表单校验不能只依赖前端,后端也要做完整校验。我在前端用一套简单的校验函数,后端的 serializer 里用 validate_xxx 方法做二次校验。这样即使有人绕过前端直接调接口,也进不来脏数据。
关于表单正则校验,分享两个实战写法:
javascript复制function isValidPhone(phone) {
return /^1[3-9]\d{9}$/.test(phone);
}
function isValidIdCard(idCard) {
// 简单校验18位身份证,末尾可能是数字或X
return /^\d{17}[\dXx]$/.test(idCard);
}
身份证校验网上有更严谨的加权因子算法,但做毕业设计或内部系统,正则加长度校验基本够用了。真正上线的话,建议引入完整的 GB11643 校验,能拦住大部分手滑填错的身份证号。
4.4 报到码展示与管理员扫码
学生端在预报到审核通过后,会进入一个报到码页面。我用 weapp-qrcode 在 canvas 上绘制二维码。这个库使用简单,核心思路是把二维码内容传给 drawCanvas 方法,在 canvas 组件上渲染。
需要注意一个问题:canvas 的绘制是异步的,在小程序里 canvas 2d 接口的初始化要在 onReady 之后执行,不要在 onLoad 里直接绘制,否则会出现空白二维码。
管理员扫码的页面相对简单,调用 wx.scanCode 得到 result 后请求后端接口。这里有个小经验,wx.scanCode 的返回值 result 就是二维码的内容,如果我们是直接把 report_code 作为二维码内容,那么扫码后的逻辑就是拿 result 去查库。但这样有个隐患,如果别人把 report_code 复制出来发给别人,一样可以核销。所以我建议二维码内容做成一个带签名的 URL 或者拼接 token 的字符串,比如 https://example.com/report/verify?code=8A3K2M&sign=xxx,管理员扫码只识别这个 URL,后端再验签。学生端展示的二维码也不用担心被截图分享后滥用,因为验签不通过。
5. 完整流程串联与项目部署
5.1 从学生端到管理端的全流程串联
整个系统的流程串起来是这样的:
- 学校管理员在 Django Admin 后台导入新生录取名单,包含姓名、身份证号、录取专业、报到批次。
- 学生在微信中打开小程序,微信授权登录后,后端根据 openid 关联到该学生的录取信息。
- 学生查看录取信息无误后,进入预报到表单页,填写基本资料、到校时间、服装尺码等。
- 学生提交预报到,后端生成一条待审核的报到记录。
- 学院管理员在后台或小程序管理端看到待审核列表,逐条审核或批量通过。
- 审核通过后,学生端开放报到码页面,学生到了学校出示二维码。
- 现场的学院管理员用小程序扫码,后端校验状态后完成正式报到,记录报到时间。
- 校级管理员在统计看板查看各学院报到率、各时段到校人数分布等数据。
这条链路覆盖了从数据准备、线上预报到、线下核销到数据统计的完整闭环。每一步的状态变化都有据可查,出了纠纷也能通过 Admin 后台的操作日志回溯。
5.2 从Django后端部署到线上
本地开发跑 python manage.py runserver 是一回事,上线部署是另一回事。Django 应用本身不是一个可以直接对外提供服务的进程,需要通过 WSGI 服务器(如 gunicorn、uwsgi)来运行。
我个人的推荐是 Nginx + gunicorn + Django 的组合。Nginx 负责处理静态文件、反向代理、HTTPS 证书;gunicorn 负责运行 Django 应用。部署步骤大致如下:
先在服务器上安装 Python、创建虚拟环境:
bash复制python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
然后收集静态文件并迁移数据库:
bash复制python manage.py collectstatic --noinput
python manage.py migrate
启动 gunicorn:
bash复制gunicorn config.wsgi:application -w 4 -b 127.0.0.1:8000
这里 -w 4 表示启动4个 worker 进程,具体数量可以根据服务器 CPU 核数调整,一般每个核跑2到4个 worker 比较合理。如果访问量不大,2个 worker 足够。
接着在 Nginx 配置里加一个 server 块,把 80 端口和 443 端口的流量转给 127.0.0.1:8000,并且配置好域名和 HTTPS 证书。微信小程序要求正式环境必须 HTTPS,证书可以用云服务商的免费证书,配置起来不复杂。
如果你用的是宝塔面板,过程更省心。创建站点,设置 Python 项目,填入 gunicorn 启动命令,配置反向代理即可。我第一次在宝塔上部署 Django 时踩过一个坑:宝塔默认的 Python 项目管理器对项目目录有权限限制,需要手动把项目目录的属主改成 www 用户,否则静态文件可能没有读取权限。
5.3 关于跨域问题的处理
本来前后端分离架构都会遇到 CORS 跨域问题,但微信小程序有个特点——它不是浏览器环境,没有同源限制,所以不需要处理 CORS。这点和 Web 前端开发很不一样,少了一个大坑。
但如果你的管理端有 Web 页面,比如用 Django Admin 或者独立的管理后台,那就需要配置 django-cors-headers。我的做法是给 /api/ 路径单独加白名单,不要让所有域名都能跨域访问你的接口。
还有一个值得注意的问题,小程序的请求 referer 固定是 https://servicewechat.com/...,后端如果需要根据 referer 做安全校验,可以把这个特征加进去。但不要完全依赖它,因为模拟器和真机环境会有差异。
6. 常见问题与排查技巧实录
6.1 微信登录时 code 换取 openid 失败
这个是最常见的问题。报错一般有两种:一种是 invalid code,提示 code 无效或过期;另一种是 invalid appid,提示 appid 不存在或与 secret 不匹配。
invalid code 的原因通常是 code 被使用了两次,或者 code 过期。wx.login 生成的 code 5分钟内有效,且只能换取一次 session_key,如果第一次请求因网络超时没有拿到响应,千万不要用同一个 code 去重试,要重新调用 wx.login。
invalid appid 就检查前端小程序的 AppID 和后端配置的 WeChat AppID 是否一致。有的人本地开了多个小程序项目,后端的 AppID 配的是另一个项目的,自然就失败了。
调试的时候可以在后端视图里临时打印一下微信接口的完整返回内容,不用急着封装成友好提示。看到原始报错才能定位问题。
6.2 图片上传失败或上传后无法访问
这个项目里学生需要上传证件照,如果你也遇到上传失败,先检查三个地方:
- Django 配置里 MEDIA_ROOT 是否指向了你想要的目录,MEDIA_URL 是否设置了。
- 本地开发时是否在 urls.py 里配置了媒体文件的路由。加一行
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)即可。 - 服务器部署时,Nginx 是否配置了 media 目录的 location。
如果是上传图片大小超过限制,通常 Nginx 默认的 client_max_body_size 是1M,需要改成比如说 10M:
nginx复制client_max_body_size 10m;
Django 这边的 DATA_UPLOAD_MAX_MEMORY_SIZE 也要同步调整。
6.3 小程序真机测试时请求失败
真机上网络请求报错,最常见的就是 HTTPS 证书问题和合法域名问题。如果你的域名还没有配置 SSL 证书,或者证书链不完整,真机访问就会失败。解决办法就是给域名配上有效的 SSL 证书,并在微信公众平台的小程序后台把请求域名加入“request 合法域名”。
另外,真机测试时 net::ERR_CONNECTION_RESET 这个错误,多半是请求的域名在国外或网络链路不稳定,也有可能是服务器防火墙拦截了来自微信服务器的请求。检查一下服务器的安全组策略,确认 443 端口对公网开放,确认防火墙没有把微信服务器的 IP 地址段屏蔽掉。
6.4 Django 的 DEBUG 开关与安全配置
上线部署时一定记得把 DEBUG 设为 False,并配置 ALLOWED_HOSTS。如果 DEBUG 不关,任何人访问你的域名只要触发报错页面,就会把项目路径、数据库配置都泄露出来,属于严重的安全隐患。
DEBUG=False 之后还有一个连带问题:Django 不再自动处理静态文件,这就是为什么部署时要执行 collectstatic 并用 Nginx 托管静态文件。我在第一次上线时就是因为这个,Admin 后台的样式全丢了,排查了半天才发现是 Nginx 没有配置静态文件路径。
6.5 时间字段始终慢8小时
开发过程中你会发现接口返回的时间跟北京时间差8个小时。前面提过,这是 USE_TZ 导致的。在 Django 里,如果 USE_TZ=True,时间会以 UTC 存储,读取时如果你没做本地化转换,前端展示出来的就是 UTC 时间。
两种解决思路:一是把 settings.py 中的 TIME_ZONE 设为 Asia/Shanghai,并在序列化输出时间字段时用 localtime 转换;二是在数据库连接层面直接改时区。第二种方式需要看你用的数据库,MySQL 可以在连接参数里加 time_zone 设置,但不如第一种通用。我在项目里用的是第一种,简单直接,代码里不用到处加转换逻辑。
7. 关于这个项目还可以继续扩展的方向
项目做到这一步,已经是一个能跑的完整版本,但真正从毕业设计走向生产环境,还有几个值得优化的点。
第一个是消息通知闭环。目前学生提交预报向后,只能在小程序里刷新状态查看审核结果,体验一般。可以接入微信订阅消息,管理员审核通过后给学生推一条模板消息,学生点开直接跳到报到码页面。配置不复杂,在微信公众平台申请模板,后端在审核通过时调用订阅消息接口即可。
第二个是一键导出与数据对接。报到数据最终要同步到学校的教务系统或学工系统,方便后续的宿舍分配、军训编队、缴费核对。可以开发一个导出 Excel 的功能,也可以用 Celery 做定时任务,把当天的报到数据定时推送到学校的消息队列。
第三个是现场大屏数据可视化。报到大厅放一个 LED 屏,实时展示当前报到人数、各学院报到率、男女比例、生源地省份分布,这个做起来不难,管理端统计数据接口已经有了,前端用 ECharts 或小程序端 canvas 绘制大屏页面就行。这个小功能在展示和答辩时特别加分。
第四个是人脸识别核验。如果学校硬件条件允许,可以对接第三方人脸识别服务,现场扫码时同时调用摄像头做人脸比对,进一步提升报到核验的安全等级。但这个是锦上添花,不要一开始就做,先把基础流程跑通。
最后说说我做完整个项目之后的个人体会。新生报到系统这种业务,技术难度不算高,真正花时间的是业务流程梳理。你在开发之前至少要跟学校的管理员聊一次,搞清楚他们从拿到录取名单到现场核验整个过程中,每一步的负责人是谁、要什么数据、需要什么权责边界。把这些理清了,代码写起来其实很快。反过来,如果一开始就闷头写代码,做完再返工的概率很高。所以我的建议是:先画流程图,再建表,最后才是写接口和页面。顺序对了,项目就顺了。
