简介:一份面向计算机专业本科生毕业设计与课程实践的岗位可视化分析系统文档,聚焦招聘信息管理、求职者画像与岗位推荐等场景,给出基于Django、B/S模式、MySQL与Tomcat的系统设计与实现思路。文档围绕绪论、系统设计与环境配置、数据库设计、功能模块实现、系统测试与评估、结论六部分展开,涵盖三层架构划分、用户与岗位及简历数据模型设计、岗位发布与关键词搜索、个性化岗位推荐,以及岗位热度、行业趋势等指标的可视化呈现,并附单元测试、性能测试与用户体验评估方法。压缩包内为1个docx文件,大小约1.45MB,结构完整,便于按章节查阅与对照撰写。已有229人学习,适合需要参考完整毕设框架、功能模块划分与论文组织方式的学生及初级开发者使用。
1. 环境先对齐:这套 Django+MySQL 岗位可视化系统到底该装在哪个 Python 版本上
第一次装岗位可视化分析系统,我图省事直接用了机器上自带的 Python 3.12,结果第一步在pip install mysqlclient就报了一屏编译错误,接着前端npm install又卡在 node-sass 上,前后折腾了一个下午。后来换了 3.10,同一个 requirements.txt 再也没出过问题:这套 Django + MySQL + Vue 的岗位可视化分析系统环境搭建,难点不在代码,在版本对齐。装不对,页面能打开但数据进不去,登录能进但列表空白,排查起来全是难以定位的问题。这篇笔记按「装什么版本 → 怎么装 → 配什么参数 → 坏了怎么救」走一遍,新手能照着跑通,熟手能直接抄版本对照和参数取舍。
2. Python 与 Django 安装:从解释器到虚拟环境的三层隔离
Python 装得好不好,决定后面两天的效率。这台机器上同时有几个老项目,最忌讳的就是把 Django 直接pip install到系统解释器里,装完之后所有项目共用一个环境,任何一个包升级都可能让别的项目崩。我一般先定解释器版本,再建虚拟环境,最后才装依赖,三层隔离,出问题可以整层删掉重来。
2.1 解释器版本:3.8 到 3.10 之间闭眼选,别一上来就用最新版
这类基于 Django 的岗位可视化分析系统,绝大部分工程骨架是用 Django 3.2 LTS 拉起来的,配套的mysqlclient、Pillow、django-cors-headers也都是那一代的版本。Django 3.2 官方支持的 Python 范围到 3.10 为止,再往上就要同时升 Django,连带models.py里的default_app_config之类写法也要改,得不偿失。
我一般的取舍是这样:
| 组件 | 推荐版本 | 说明 |
|---|---|---|
| Python | 3.10.x | 3.8 也能跑,3.10 轮子最全;3.11/3.12 上老版 mysqlclient 要临时编译 |
| Django | 3.2 LTS | 与绝大多数此类工程骨架一致,改动最少 |
| mysqlclient | 2.1.x | 与 Django 3.2 配合稳定,2.2 起才完整支持 3.12 |
| MySQL | 8.0.x | 5.7 也能用,8.0 的窗口函数对后续做岗位统计更友好 |
| Node.js | 16.x / 18.x | 前端构建依赖,20 以上部分老依赖会报 engine 不匹配 |
| Tomcat | 9.x | 承载前端静态包,不要上 Tomcat 10(Servlet 5 包名变了) |
装完之后先验一次,别急着往下走:
# 确认解释器版本和 pip 归属,两条命令必须指向同一个路径 python --version python -m pip --version # 输出示例:Python 3.10.11 / pip 23.x from .../site-packages/pip (python 3.10)这里的关键是python -m pip而不是直接敲pip。很多机器上pip指向的是另一个 Python,装完包在当前解释器里找不到,报ModuleNotFoundError: No module named 'django',查半天才发现装错地方了。后面所有安装命令我都用python -m pip的写法,就是为了避开这个。
Windows 上装 Python 记得勾选Add Python to PATH,没勾的话手动把安装目录和Scripts子目录加进系统变量,否则 cmd 里敲python会跳转到应用商店。Linux 上用系统包管理器装的 Python 通常缺python3-venv和python3-dev,这两个不装上,虚拟环境建不起来、C 扩展也编译不了,常见做法是sudo apt install python3.10-venv python3.10-dev一步装齐。
2.2 虚拟环境和依赖:requirements.txt 里那几行别随手改
解释器定好之后,在工程根目录建虚拟环境。目录名统一用venv,加进.gitignore,不要提交。
# 在工程根目录创建虚拟环境 python -m venv venv # Windows 激活 venv\Scripts\activate # Linux / macOS 激活 source venv/bin/activate # 激活成功后命令行前面会出现 (venv),再验一次归属 python -c "import sys; print(sys.executable)"python -m venv venv第一个venv是模块名,第二个是目录名,两者同名容易看混但它确实就是这么写的。激活这一步在 Windows PowerShell 下如果报「无法加载文件,因为在此系统上禁止运行脚本」,是执行策略的问题,用Set-ExecutionPolicy -Scope CurrentUser RemoteSigned放开当前用户即可,别去改全局策略。
工程里那份requirements.txt大致长这样,版本号别随手写latest:
Django==3.2.25 mysqlclient==2.1.1 django-cors-headers==3.14.0 Pillow==9.5.0 requests==2.31.0 waitress==2.1.2安装时就一条命令:
# 用国内镜像加速,换行写成一行也行,失败了再重试 python -m pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple # 验证核心依赖是否都能 import python -c "import django, MySQLdb, PIL; print(django.get_version())"几个参数值得说清楚。-i指定源,只在装不动的时候加,长期用建议写进pip.ini,别每次敲。mysqlclient是 Django 连 MySQL 的默认驱动,Windows 上如果没有预编译轮子,会尝试本地编译并索取 Visual C++ Build Tools,这是最容易失败的一步,失败时先别急着装几个 G 的构建工具,直接看第 5 章的兜底方案。waitress是纯 Python 的 WSGI 服务器,Windows 上比 uWSGI 好装得多,后面第 4 章起后端就靠它。
装完之后跑一次python manage.py check,它会把 settings 里明显的配置错误提前报出来,比启动服务之后再撞墙高效。
2.3 settings.py 里五个必须改的配置项
依赖装好,接下来是配置。工程里的settings.py是模板改出来的,直接跑大概率连不上库,下面这几项必须按本机情况改。
# settings.py import os # 1. 允许访问的主机,开发阶段留空会导致 400 ALLOWED_HOSTS = ['127.0.0.1', 'localhost'] # 2. 数据库连接,四要素按第 3 章建好的库填 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'job_visual', # 库名,与建库语句保持一致 'USER': 'jobuser', # 单独建的业务账号,不要用 root 'PASSWORD': 'Job@2024', # 生产环境改从环境变量读 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': { 'charset': 'utf8mb4', # 必须 utf8mb4,否则中文和表情存不进去 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", }, } } # 3. 语言和时区,改这两项影响后台时间显示 LANGUAGE_CODE = 'zh-hans' TIME_ZONE = 'Asia/Shanghai' USE_TZ = False # 关掉 UTC,否则列表时间差 8 小时 # 4. 静态文件和上传目录,前端打包产物和头像都落在这 STATIC_URL = '/static/' STATIC_ROOT = os.path.join(BASE_DIR, 'static') MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 5. 跨域,前后端不同端口时必须开 CORS_ALLOW_ALL_ORIGINS = True # 仅开发期,上线改成白名单逐项解释。ALLOWED_HOSTS为空时 Django 只放行localhost,用局域网 IP 访问会返回 400,调试阶段经常被这个挡住。NAME要和第 3 章CREATE DATABASE的名字一字不差,包括下划线。USER不要图省事用 root,这类系统的表里有手机号、简历信息,单开一个只对job_visual有增删改查权限的账号,后面换机器迁移也只需要改这一处。OPTIONS里的charset必须显式写utf8mb4,靠库的默认字符集有时候不够,尤其是从别的机器导数据过来的时候。
USE_TZ = False这一项需要单独说。Django 默认把时间按 UTC 存、按TIME_ZONE显示,听着合理,但这类工程里的addtime字段是 MySQL 的CURRENT_TIMESTAMP直接写库的,数据库那边本来就是北京时间,Django 再按 UTC 解释一遍,页面上就慢 8 小时。要么全程 UTC,要么全程本地时间,这类项目我一般直接关掉,省得后面两头对不上。
CORS_ALLOW_ALL_ORIGINS同上,开发阶段省事,上线前必须改成CORS_ALLOWED_ORIGINS = ['http://你的域名'],并且要在INSTALLED_APPS里加'corsheaders'、在MIDDLEWARE靠前的位置加'corsheaders.middleware.CorsMiddleware',只装包不加中间件是不生效的。
3. MySQL 8.0 建库与表结构:字符集、主键、时间字段一次写对
数据库这一层是整套系统里最容易返工的地方。表建错了可以 drop 重来,但字符集设错、字段长度给短,等到导数据的时候才发现,就得重新建库。我的习惯是建库建表全部写成 SQL 文件存进工程,换机器直接source一遍,比用图形界面点着建可复现得多。
3.1 装 MySQL 8.0 并绕开认证插件那个坑
MySQL 8.0 默认用caching_sha2_password认证,老版本的mysqlclient和一些图形客户端连上去会报Authentication plugin 'caching_sha2_password' cannot be loaded。这个错误的解决方式不是降级 MySQL,而是给业务账号单独指定认证插件。
# Ubuntu 上装 8.0(Windows 用官方安装包,选 Server only 即可) sudo apt update sudo apt install mysql-server-8.0 # 启动并设成开机自启 sudo systemctl enable --now mysql # 首次初始化,按提示设 root 密码,一路选 y sudo mysql_secure_installation装完之后进 MySQL 建业务账号,认证插件明确写成mysql_native_password:
-- 建业务账号并指定认证插件,避开 caching_sha2_password 兼容问题 CREATE USER 'jobuser'@'127.0.0.1' IDENTIFIED WITH mysql_native_password BY 'Job@2024'; -- 只给这一个库的权限,不给全局 GRANT ALL PRIVILEGES ON job_visual.* TO 'jobuser'@'127.0.0.1'; FLUSH PRIVILEGES;IDENTIFIED WITH后面跟的插件名是关键,不写就用默认值。'jobuser'@'127.0.0.1'里的主机部分决定了从哪连:写127.0.0.1走 TCP,写localhost走 socket,Django 的HOST填的是127.0.0.1,这里就必须写127.0.0.1,写成localhost会出现「账号密码都对但连不上」的情况。GRANT只授到job_visual.*,不给*.*,也别顺手加WITH GRANT OPTION。
3.2 建库与六张核心表:字段类型按实际数据量给
库建好之后建表。这套岗位可视化分析系统的核心表在文档里列得很清楚,招聘信息、用户、管理账号、地区、关于我们、配置,一张张写下来。
-- 建库,字符集必须 utf8mb4,排序规则用 8.0 默认的 0900_ai_ci CREATE DATABASE IF NOT EXISTS job_visual DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE job_visual; -- 招聘信息表 CREATE TABLE zhaopinxinxi ( id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键', addtime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', chengshien VARCHAR(200) DEFAULT NULL COMMENT '城市英文名', fabuhao VARCHAR(200) DEFAULT NULL COMMENT '发布号', zhiwei VARCHAR(200) DEFAULT NULL COMMENT '职位', xueli VARCHAR(200) DEFAULT NULL COMMENT '学历', jingyan VARCHAR(200) DEFAULT NULL COMMENT '经验', xinzi VARCHAR(200) DEFAULT NULL COMMENT '薪资', gongsi VARCHAR(200) DEFAULT NULL COMMENT '公司', gongzuodidian VARCHAR(200) DEFAULT NULL COMMENT '工作地点', gongsileixing VARCHAR(200) DEFAULT NULL COMMENT '公司类型', detail LONGTEXT COMMENT '职位描述', fuli VARCHAR(200) DEFAULT NULL COMMENT '福利', PRIMARY KEY (id), KEY idx_zhiwei (zhiwei), -- 岗位检索按名称查,加普通索引 KEY idx_diqu (gongzuodidian) -- 地区筛选同样高频 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='招聘信息'; -- 求职用户表 CREATE TABLE yonghu ( id BIGINT NOT NULL AUTO_INCREMENT, addtime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, yonghuming VARCHAR(200) DEFAULT NULL COMMENT '用户名', mima VARCHAR(200) DEFAULT NULL COMMENT '密码', xingming VARCHAR(200) DEFAULT NULL COMMENT '姓名', touxiang LONGTEXT COMMENT '头像', xingbie VARCHAR(200) DEFAULT NULL COMMENT '性别', nianling INT DEFAULT NULL COMMENT '年龄', shouji VARCHAR(200) DEFAULT NULL COMMENT '手机', diqu VARCHAR(200) DEFAULT NULL COMMENT '地区', PRIMARY KEY (id), UNIQUE KEY uk_yonghuming (yonghuming) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户'; -- 管理账号表 CREATE TABLE users ( id BIGINT NOT NULL AUTO_INCREMENT, username VARCHAR(100) DEFAULT NULL COMMENT '用户名', password VARCHAR(100) DEFAULT NULL COMMENT '密码', role VARCHAR(100) DEFAULT NULL COMMENT '角色', addtime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_username (username) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; -- 地区表、关于我们表、配置表结构相对简单,一起建 CREATE TABLE diqu ( id BIGINT NOT NULL AUTO_INCREMENT, addtime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, diqu VARCHAR(200) DEFAULT NULL, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='地区'; CREATE TABLE systemintro ( id BIGINT NOT NULL AUTO_INCREMENT, addtime TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, title VARCHAR(200) DEFAULT NULL, subtitle VARCHAR(200) DEFAULT NULL, content LONGTEXT, picture1 LONGTEXT, picture2 LONGTEXT, picture3 LONGTEXT, PRIMARY KEY (id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='关于我们';几处取舍值得说。detail用LONGTEXT不用TEXT,因为职位描述带富文本编辑器出来的 HTML,TEXT的 64KB 上限在长 JD 上会截断,报Data too long的错误,改成LONGTEXT之后就没再管过。xinzi薪资用VARCHAR存,不是数值类型,因为招聘数据里的薪资大多是「15-25K·14薪」这种区间字符串,拆成两个数值字段反而丢信息,做可视化统计时在前端解析即可。addtime统一TIMESTAMP DEFAULT CURRENT_TIMESTAMP,插入时不用管,也让排序有依据。
索引不是越多越好。zhaopinxinxi上只给zhiwei和gongzuodidian加了普通索引,因为列表页的高频操作就是按岗位名搜索、按地区筛选;xueli、jingyan这类低基数字段加了意义不大,反而拖慢写入。yonghuming和username加了唯一索引,防止重复注册。
3.3 让 Django 认下已有表:inspectdb 和 migrate 的先后顺序
表已经建好了,Django 那边还要有对应的模型类。这类工程常见的做法是用inspectdb从现有表反向生成模型,再手工整理。
# 反向生成模型到临时文件,先看再改 python manage.py inspectdb > app/models_tmp.py # 只针对某几张表生成 python manage.py inspectdb zhaopinxinxi yonghu users diqu systemintro > app/models_tmp.py生成出来的模型有个坑:每个Meta里都会带managed = False,意思是 Django 不管这张表的建表和迁移。表是我们手工建好的,这个标志保留没问题,但后续如果想加字段就必须手动改表,migrate不会帮忙。反过来,如果打算让 Django 托管表结构,就要把managed删掉,并且第一次要先makemigrations再migrate,不能先建表。两条路都行,但必须选一条,混着用会出现「迁移文件里没有这张表,但代码里在查它」的问题。
# app/models.py 整理后的样子,字段名与表一一对应 class Zhaopinxinxi(models.Model): addtime = models.DateTimeField(auto_now_add=True) zhiwei = models.CharField(max_length=200, blank=True, null=True) gongzuodidian = models.CharField(max_length=200, blank=True, null=True) detail = models.TextField(blank=True, null=True) class Meta: managed = False # 表已手工建好,Django 不参与建表 db_table = 'zhaopinxinxi' # 必须显式写表名,不能靠自动推导db_table一定要写死,否则 Django 会按应用名_模型名小写的规则去猜表名,猜出来的和实际的zhaopinxinxi对不上,查询直接报Table doesn't exist。这一步也是我见过最多人卡住的地方,明明表在,Django 就是说没有。
4. 前端构建与 Tomcat 9 部署:静态包放哪、接口往哪打
这套系统的部署形态我一般是这么分的:Vue 打包出来的静态资源交给 Tomcat 承载,Django 后端用 waitress 单独起一个进程,两边通过 HTTP 接口通信。这里要说清楚一件事——Django 是 WSGI 应用,Tomcat 是 Servlet 容器,两者不能互相加载,强行把 Python 代码塞进 Tomcat 是行不通的。
4.1 Node 版本与 Vue 依赖安装:先把 node-sass 这个坑绕开
前端目录拿到手,第一件事不是npm install,是先确认 Node 版本。
# 确认版本,16 或 18 都可以,20 以上部分老依赖会报 engine 不匹配 node -v npm -v # 有 nvm 的话切版本最省事 nvm install 18 nvm use 18 # 清掉可能存在的旧缓存和旧依赖 rm -rf node_modules package-lock.json npm cache clean --force # 安装依赖,用国内源 npm install --registry=https://registry.npmmirror.comnode_modules和package-lock.json一起删是有讲究的。锁文件是在某个 Node 版本下生成的,换了版本之后里面记录的依赖树可能和新版本不兼容,留着反而会装出一堆玄学问题。删掉重新装一次,虽然慢几十秒,但省下来的排查时间远不止。
如果npm install卡在node-sass上并报Node Sass could not find a binding,那是版本和 Node 对不上。这类工程里node-sass一般出现在老版本的构建工具链里,处理方式有两种:换成sass(纯 JS 实现,不需要编译),或者在package.json里把node-sass的版本调到与当前 Node 匹配的那一版。我一般选前者,一劳永逸。
4.2 打包路径和后端地址:两个地方必须和部署环境对齐
npm run build出包之前,有两个配置项要先改,否则包放上去就是白屏或者接口 404。
// vue.config.js module.exports = { // 部署到 Tomcat 的 /job 路径下,这里就写 /job/ // 写错成默认的 / 会导致所有静态资源请求打到根路径而 404 publicPath: process.env.NODE_ENV === 'production' ? '/job/' : '/', // 开发期代理,把 /api 转到本机 8000 的 Django devServer: { port: 8081, proxy: { '/api': { target: 'http://127.0.0.1:8000', changeOrigin: true, pathRewrite: { '^/api': '' } // 后端没有 /api 前缀就重写掉 } } } }// src/utils/request.js,axios 的 baseURL 按环境区分 import axios from 'axios' const request = axios.create({ // 开发期走 devServer 代理,生产期直接指向后端服务地址 baseURL: process.env.NODE_ENV === 'development' ? '/api' : 'http://127.0.0.1:8000', timeout: 10000 }) export default requestpublicPath是最容易翻车的一项。包打成/之后,index.html里引用的 JS 路径是/js/app.js,而应用实际部署在http://host:8080/job/,浏览器会去请求http://host:8080/js/app.js,自然是 404。改成/job/之后路径就对了。判断依据很简单:打开浏览器控制台看 Network,如果 JS、CSS 全是 404,先查这一项。
baseURL的取舍在于,生产环境如果前后端同机同端口(Tomcat 反代到 Django),写相对路径/api更稳;如果是分开的两个端口,就得写完整地址,并且后端要开 CORS。我一般在前端这台机器上不做反代,直接写全地址,配置少一层,出问题好定位。
4.3 Tomcat 9 部署静态包:路径、端口、权限三件事
打包产物在dist目录,把整个目录拷进 Tomcat 的webapps下并重命名为job,这是最省事的部署方式。
# 打包 npm run build # 拷到 Tomcat 应用目录,目录名就是访问路径 rm -rf /opt/tomcat9/webapps/job cp -r dist /opt/tomcat9/webapps/job # 重启 Tomcat /opt/tomcat9/bin/shutdown.sh /opt/tomcat9/bin/startup.sh # 看日志确认部署成功 tail -f /opt/tomcat9/logs/catalina.out访问http://127.0.0.1:8080/job/应该能看到首页。如果返回 404,先确认webapps/job下有没有index.html;如果有却还是 404,检查conf/web.xml里的welcome-file-list是否被改过。
端口冲突是另一类常见情况。8080 被别的服务占了,Tomcat 启动会报Address already in use,要么改端口,要么停掉占用方:
<!-- conf/server.xml,把 8080 改成 8088 --> <Connector port="8088" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />改了端口之后,前端request.js里的地址、以及任何硬编码的 URL 都要同步改。这类硬编码分散在好几个文件里,改之前先全局搜一遍8080,一次改全。
后端这边用 waitress 起服务,Windows 和 Linux 都能跑:
# 在虚拟环境里,进入 Django 工程根目录 # --listen 指定监听地址和端口,--threads 按并发量调 waitress-serve --listen=0.0.0.0:8000 --threads=8 jobvisual.wsgi:applicationjobvisual.wsgi:application里的前半段是wsgi.py所在包的目录名,不是工程里的应用名,写错了会报ModuleNotFoundError。--threads=8对一个演示性质的岗位分析系统足够用,真要压测再往上加,盲目加到几十反而因为 GIL 争抢变慢。这个进程要常驻的话,Linux 上写 systemd 单元,Windows 上用 nssm 注册成服务,别开着黑窗口跑。
5. 常见问题排查:五条踩过的坑与现场处理
安装过程中踩过的坑,我把印象最深的五条记在这。每条按「什么现象、为什么、怎么解」写,遇到了直接对号入座。
5.1 现象:mysqlclient 安装报一屏编译错误
现象:pip install mysqlclient输出几十行红色,末尾是error: Microsoft Visual C++ 14.0 or greater is required,或者fatal error: Python.h: No such file or directory。
原因:这个包是 C 扩展,pip 找不到匹配的预编译轮子时会退回到本地编译,本地缺编译器和 Python 开发头文件就必然失败。Python 版本越新,能匹配到的轮子越少,3.12 上尤其明显。
解决:两条路。第一条是换回 3.10 重装,绝大多数情况下直接成功。第二条是实在不想换版本,用纯 Python 实现的 PyMySQL 顶替,在工程的__init__.py里加两行:
# jobvisual/__init__.py import pymysql pymysql.install_as_MySQLdb() # 让 Django 把 PyMySQL 当成 MySQLdb 用同时在requirements.txt里把mysqlclient换成PyMySQL==1.1.0。这个方案在功能上没有差别,只是性能略低,对这类系统完全够用。Linux 上如果报Python.h找不到,装python3.10-dev即可。
5.2 现象:写入中文时报 1366 错误
现象:后台新增一条招聘信息,页面提示Incorrect string value: '\xE5\x8C\x97\xE4\xBA\xAC' for column 'gongzuodidian',错误码 1366。
原因:库、表、连接三处字符集有一处不是utf8mb4。最常见的是建库时用了默认字符集,或者连接串里没写charset,MySQL 按latin1解释客户端发来的字节。
解决:三处一起查、一起改:
-- 查库和表的字符集 SELECT DEFAULT_CHARACTER_SET_NAME FROM information_schema.SCHEMATA WHERE SCHEMA_NAME='job_visual'; SHOW CREATE TABLE zhaopinxinxi; -- 已建好的表改字符集 ALTER TABLE zhaopinxinxi CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;然后确认settings.py的OPTIONS里有'charset': 'utf8mb4'。三处都对齐之后重启 Django 进程。注意utf8和utf8mb4在 MySQL 里不是一回事,前者最多 3 字节,存不了 emoji,别名utf8mb3,别图短写utf8。
5.3 现象:前端包部署后页面空白,控制台全是 404
现象:访问http://host:8080/job/页面标题出来了,但内容区一片空白,F12 里十几条GET /js/chunk-vendors.js 404。
原因:publicPath没改,还是打包默认的/。也可能拷贝的时候把dist里的内容多套了一层目录,变成webapps/job/dist/index.html。
解决:先看webapps/job下是不是直接就是index.html,多套了一层就把里层内容挪上来。确认目录结构没错,再回去改vue.config.js的publicPath为/job/,重新npm run build。改完记得清一次浏览器缓存,Ctrl+F5强刷,因为index.html可能被缓存住还引用着旧路径。
5.4 现象:接口报 CORS 错误,OPTIONS 请求 403
现象:登录按钮点下去没反应,控制台报has been blocked by CORS policy,Network 里那条请求的方法是OPTIONS,状态码 403。
原因:前后端不同端口就构成跨域,浏览器先发OPTIONS预检。Django 没配 CORS 中间件,或者配了但中间件顺序不对,预检被 CSRF 拦下来。
解决:确认三件事都做了——INSTALLED_APPS里有'corsheaders'、MIDDLEWARE里'corsheaders.middleware.CorsMiddleware'放在'django.middleware.common.CommonMiddleware'之前、CORS_ALLOW_ALL_ORIGINS = True。三条里漏任何一条都不生效。如果坚持要开着 CSRF,就把接口类加上@csrf_exempt,或者前端请求带上X-CSRFToken头,两者选一,别都不做。
5.5 现象:列表里的时间比实际慢 8 小时
现象:数据库里查出来是2024-05-20 14:30:00,页面上显示2024-05-20 06:30:00。
原因:USE_TZ = True且TIME_ZONE还是默认的America/Chicago或者没改,Django 把数据库里的时间当成本地时间读进来,再按 UTC 解释一遍。
解决:把TIME_ZONE改成'Asia/Shanghai',USE_TZ设为False,重启进程。这里的关键是数据库和 Django 只能用同一种时间语义,两边不一致就会出现固定偏移,而且偏移量随夏令时变化,属于典型的最难排查的问题。改完之后已有的数据不用动,因为数据库里存的本来就是对的时间。
6. 装完怎么验证跑通:一份五分钟自检清单
环境装完,最忌讳的是「页面能打开就算成功」。这类系统真正跑通的标准是:能登录、能写入、能查出、图表有数。我一般在交付前跑一遍下面这套流程,五分钟之内能把八成问题拦下来。
第一步查进程和端口,三个服务必须都在:
# Windows netstat -ano | findstr "3306 8000 8080" # Linux ss -lntp | grep -E '3306|8000|8080'3306 是 MySQL,8000 是 waitress 起的 Django,8080 是 Tomcat,三个端口缺一个就往下查对应的服务日志。MySQL 起不来看error.log,Django 起不来看控制台第一段报错,Tomcat 起不来看logs/catalina.out的最后 30 行,报错基本都在里面,不用满世界搜。
第二步用一条命令验证后端连库是不是通的,绕开前端直接打接口:
# 请求招聘信息列表接口,看返回结构和数据条数 curl -s "http://127.0.0.1:8000/zhaopinxinxi/list?page=1&limit=5" | head -c 500 # 期望看到 {"code":0,"data":{"total":N,"list":[...]}} 这样的结构 # 如果返回 500,去看 Django 控制台的堆栈,八成是 db_table 写错或库名不对第三步验证写入,这是最容易被漏掉的一环。很多环境是只读通的,一写就报错。在后台新增一条招聘信息,然后回数据库确认:
-- 确认最新一条数据和中文都没问题 SELECT id, zhiwei, gongzuodidian, addtime FROM zhaopinxinxi ORDER BY id DESC LIMIT 1;第四步验证前端和接口的连通。打开http://127.0.0.1:8080/job/,按 F12 看 Network,登录一次,看登录接口是不是 200、返回的 token 有没有存下来、跳转后的列表接口有没有带 token。这一步能同时验证跨域、baseURL、路由三件事。
第五步才是看可视化。首页的岗位分布图、地区热力图要能出数,如果图出来了但是空白,多半是接口返回的字段名和 ECharts 里配的name/value对不上,去比对一下接口返回的 JSON key 即可。
我做了一张对照表放在工程根目录,每次换机器都照着过一遍:
| 检查项 | 命令/位置 | 通过标准 |
|---|---|---|
| MySQL 连接 | mysql -ujobuser -p -h127.0.0.1 | 能进job_visual库 |
| Django 检查 | python manage.py check | 输出System check identified no issues |
| 后端接口 | curl 127.0.0.1:8000/... | 返回 200 且 data 非空 |
| 前端页面 | http://host:8080/job/ | 无 404,控制台无红色报错 |
| 数据写入 | 后台新增 +SELECT回查 | 中文正常,addtime是当前时间 |
这套清单是从一次教训里固化下来的。有回我给一台新机器部署,页面打开、列表有数据,看着一切正常,交付之后对方反馈「后台加不了岗位」,一查是业务账号只给了SELECT权限,GRANT的时候少写了一句。从那以后我每次装完都强制走一遍「能读、能写、能查、图有数」这四步,一步不落地跑完才敢说装好了。
照着走一遍,卡在哪一步就回对应的章节找参数,比对着报错满世界搜快得多。希望帮到你。
本文还有配套的精品资源,点击获取