作为一名长期从事大学生项目辅导的技术博主,我经常遇到学生在毕业设计阶段面临的两个核心痛点:一是缺乏真实可用的项目源码和配套文档,二是对推荐系统这类复杂项目的实现逻辑理解不透彻。今天要分享的这个就业岗位推荐系统,正是为了解决这些问题而设计的实战型项目。
这个系统采用Python+Django作为后端基础框架,结合机器学习算法实现岗位个性化推荐,是一个典型的"大数据+推荐系统"毕业设计解决方案。系统完整实现了从用户注册登录、简历上传、岗位推荐到后台管理的全流程功能,特别适合计算机相关专业学生作为毕业设计选题。
为什么选择这个技术栈?
- Django框架成熟稳定,学习曲线平缓,适合毕业设计周期
- Python生态有丰富的机器学习库(如scikit-learn),便于实现推荐算法
- 前后端分离架构使项目结构清晰,便于扩展和维护
后端技术栈:
前端技术栈:
开发工具链:
code复制┌───────────────────────────────────────────────────┐
│ 客户端浏览器 │
│ (Vue.js + Element Plus + Axios + ECharts) │
└───────────────┬───────────────────┬───────────────┘
│ │
▼ ▼
┌───────────────────────────────────────────────────┐
│ Django REST API │
│ (认证/授权/推荐算法/业务逻辑处理) │
└───────────────┬───────────────────┬───────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ MySQL │ │ Redis │
│ (结构化数据存储)│ │ (缓存/会话管理) │
└─────────────────┘ └─────────────────┘
核心表结构设计:
用户表(users_user)
sql复制CREATE TABLE `users_user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(150) NOT NULL,
`password` varchar(128) NOT NULL,
`email` varchar(254) NOT NULL,
`user_type` smallint NOT NULL COMMENT '0-求职者 1-招聘方',
`created_at` datetime(6) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
简历表(resumes_resume)
sql复制CREATE TABLE `resumes_resume` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`title` varchar(100) NOT NULL,
`skills` text NOT NULL COMMENT '技能标签,逗号分隔',
`work_experience` text NOT NULL,
`education` text NOT NULL,
`file_path` varchar(255) DEFAULT NULL,
`created_at` datetime(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `resumes_resume_user_id_idx` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
岗位表(jobs_job)
sql复制CREATE TABLE `jobs_job` (
`id` bigint NOT NULL AUTO_INCREMENT,
`company_id` bigint NOT NULL,
`title` varchar(100) NOT NULL,
`description` text NOT NULL,
`requirements` text NOT NULL,
`salary_range` varchar(50) NOT NULL,
`location` varchar(100) NOT NULL,
`tags` varchar(255) NOT NULL COMMENT '岗位标签,逗号分隔',
`created_at` datetime(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `jobs_job_company_id_idx` (`company_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
用户行为表(users_behavior)
sql复制CREATE TABLE `users_behavior` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL,
`job_id` bigint NOT NULL,
`behavior_type` smallint NOT NULL COMMENT '1-浏览 2-收藏 3-申请',
`created_at` datetime(6) NOT NULL,
PRIMARY KEY (`id`),
KEY `users_behavior_user_id_idx` (`user_id`),
KEY `users_behavior_job_id_idx` (`job_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
数据库设计经验:
- 所有表都添加created_at字段,便于后续数据分析
- 行为数据表采用"用户ID+物品ID+行为类型"的经典设计模式
- 使用文本字段存储标签数据,实际应用中可考虑专门的标签系统
采用Django内置的认证系统扩展实现:
python复制# serializers.py
from rest_framework import serializers
from django.contrib.auth import get_user_model
from rest_framework_simplejwt.serializers import TokenObtainPairSerializer
User = get_user_model()
class UserRegisterSerializer(serializers.ModelSerializer):
password = serializers.CharField(write_only=True, required=True)
class Meta:
model = User
fields = ['username', 'email', 'password', 'user_type']
def create(self, validated_data):
user = User.objects.create_user(
username=validated_data['username'],
email=validated_data['email'],
password=validated_data['password'],
user_type=validated_data['user_type']
)
return user
class CustomTokenObtainPairSerializer(TokenObtainPairSerializer):
@classmethod
def get_token(cls, user):
token = super().get_token(user)
token['user_type'] = user.user_type
return token
python复制# views.py
from rest_framework import generics, permissions
from rest_framework.response import Response
from .serializers import UserRegisterSerializer, CustomTokenObtainPairSerializer
from rest_framework_simplejwt.views import TokenObtainPairView
class RegisterView(generics.CreateAPIView):
serializer_class = UserRegisterSerializer
permission_classes = [permissions.AllowAny]
class CustomTokenObtainPairView(TokenObtainPairView):
serializer_class = CustomTokenObtainPairSerializer
认证模块注意事项:
- 密码必须加密存储,Django的create_user方法会自动处理
- JWT token中可携带自定义claims(如user_type)
- 注册接口要做防刷处理,实际项目中可添加手机验证
采用基于用户的协同过滤算法:
python复制# recommender.py
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
from collections import defaultdict
from django.db.models import Count
class JobRecommender:
def __init__(self):
self.user_sim_matrix = None
self.user_ids = None
self.job_ids = None
def prepare_data(self):
"""从数据库加载用户-岗位交互数据"""
from users.models import Behavior
from jobs.models import Job
# 获取所有活跃用户
active_users = Behavior.objects.values('user_id').annotate(
count=Count('user_id')).order_by('-count')[:1000]
self.user_ids = [u['user_id'] for u in active_users]
# 获取热门岗位
popular_jobs = Behavior.objects.values('job_id').annotate(
count=Count('job_id')).order_by('-count')[:500]
self.job_ids = [j['job_id'] for j in popular_jobs]
# 构建用户-物品矩阵
user_job_matrix = np.zeros((len(self.user_ids), len(self.job_ids)))
user_job_dict = defaultdict(dict)
behaviors = Behavior.objects.filter(
user_id__in=self.user_ids,
job_id__in=self.job_ids
).values('user_id', 'job_id', 'behavior_type')
for b in behaviors:
user_idx = self.user_ids.index(b['user_id'])
job_idx = self.job_ids.index(b['job_id'])
# 行为加权:浏览1分,收藏3分,申请5分
user_job_matrix[user_idx][job_idx] = b['behavior_type'] * 2 - 1
# 计算用户相似度矩阵
self.user_sim_matrix = cosine_similarity(user_job_matrix)
def recommend_for_user(self, user_id, top_n=10):
"""为用户推荐岗位"""
if user_id not in self.user_ids:
# 新用户冷启动策略
return self.get_popular_jobs(top_n)
user_idx = self.user_ids.index(user_id)
sim_scores = list(enumerate(self.user_sim_matrix[user_idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
# 取最相似的20个用户
sim_users = [i for i, score in sim_scores[1:21]]
# 统计相似用户喜欢的岗位
job_scores = defaultdict(float)
for sim_user_idx in sim_users:
for job_idx, score in enumerate(self.user_sim_matrix[sim_user_idx]):
if self.user_sim_matrix[user_idx][job_idx] == 0: # 用户未交互过的岗位
job_scores[self.job_ids[job_idx]] += score
# 取评分最高的top_n个岗位
recommended_jobs = sorted(job_scores.items(),
key=lambda x: x[1],
reverse=True)[:top_n]
return [job_id for job_id, score in recommended_jobs]
def get_popular_jobs(self, top_n=10):
"""获取热门岗位(冷启动)"""
from users.models import Behavior
popular_jobs = Behavior.objects.values('job_id').annotate(
count=Count('job_id')).order_by('-count')[:top_n]
return [j['job_id'] for j in popular_jobs]
推荐算法实现要点:
- 采用余弦相似度计算用户相似度
- 对不同行为类型(浏览、收藏、申请)赋予不同权重
- 处理冷启动问题:新用户推荐热门岗位
- 实际项目中可考虑矩阵分解等更高级算法
前端通过Axios调用后端API示例:
javascript复制// api/job.js
import axios from 'axios'
const API_URL = process.env.VUE_APP_API_URL
export default {
// 获取推荐岗位
getRecommendations(userId) {
return axios.get(`${API_URL}/api/jobs/recommend/${userId}/`)
},
// 搜索岗位
searchJobs(keyword, page = 1) {
return axios.get(`${API_URL}/api/jobs/search/`, {
params: {
q: keyword,
page: page
}
})
},
// 提交岗位申请
applyJob(jobId, resumeId) {
return axios.post(`${API_URL}/api/jobs/${jobId}/apply/`, {
resume_id: resumeId
})
}
}
前端页面组件示例(Vue 3 Composition API):
vue复制<template>
<div class="job-recommendations">
<h2>为您推荐的岗位</h2>
<el-row :gutter="20">
<el-col :span="8" v-for="job in jobs" :key="job.id">
<el-card class="job-card">
<div slot="header">
<h3>{{ job.title }}</h3>
<div class="company">{{ job.company.name }}</div>
</div>
<div class="job-info">
<div><i class="el-icon-location"></i> {{ job.location }}</div>
<div><i class="el-icon-money"></i> {{ job.salary_range }}</div>
<div class="tags">
<el-tag v-for="tag in job.tags.split(',')" :key="tag">
{{ tag }}
</el-tag>
</div>
</div>
<el-button
type="primary"
@click="handleApply(job.id)"
:disabled="!hasResume">
立即申请
</el-button>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useStore } from 'vuex'
import jobAPI from '@/api/job'
export default {
setup() {
const store = useStore()
const jobs = ref([])
const hasResume = ref(false)
const fetchRecommendations = async () => {
try {
const userId = store.state.user.id
const response = await jobAPI.getRecommendations(userId)
jobs.value = response.data.results
} catch (error) {
console.error('获取推荐失败:', error)
}
}
const checkResume = () => {
hasResume.value = store.getters.hasResume
}
const handleApply = async (jobId) => {
try {
const resumeId = store.state.resume.currentResumeId
await jobAPI.applyJob(jobId, resumeId)
ElMessage.success('申请成功')
} catch (error) {
ElMessage.error('申请失败: ' + error.message)
}
}
onMounted(() => {
fetchRecommendations()
checkResume()
})
return { jobs, hasResume, handleApply }
}
}
</script>
推荐使用Docker容器化部署:
dockerfile复制# Dockerfile
FROM python:3.9-slim
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
RUN apt-get update && apt-get install -y \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "config.wsgi"]
yaml复制# docker-compose.yml
version: '3.8'
services:
web:
build: .
command: gunicorn --bind 0.0.0.0:8000 --workers 4 config.wsgi
volumes:
- .:/app
ports:
- "8000:8000"
depends_on:
- redis
- db
environment:
- DJANGO_SETTINGS_MODULE=config.settings.production
- DATABASE_URL=postgres://postgres:postgres@db:5432/postgres
- REDIS_URL=redis://redis:6379/0
db:
image: postgres:13
volumes:
- postgres_data:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
- POSTGRES_DB=postgres
redis:
image: redis:6
nginx:
image: nginx:1.21
ports:
- "80:80"
volumes:
- ./config/nginx:/etc/nginx/conf.d
- ./staticfiles:/static
depends_on:
- web
volumes:
postgres_data:
使用Locust进行压力测试:
python复制# locustfile.py
from locust import HttpUser, task, between
class JobRecommendUser(HttpUser):
wait_time = between(1, 3)
@task
def get_recommendations(self):
headers = {"Authorization": "Bearer {token}"}
self.client.get("/api/jobs/recommend/1/", headers=headers)
@task(3)
def search_jobs(self):
self.client.get("/api/jobs/search/?q=工程师")
测试结果:
Django安全中间件配置:
python复制# settings/production.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'csp.middleware.CSPMiddleware',
]
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True
SECURE_SSL_REDIRECT = True
CSP_DEFAULT_SRC = ("'self'",)
API限流配置:
python复制# settings.py
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.AnonRateThrottle',
'rest_framework.throttling.UserRateThrottle'
],
'DEFAULT_THROTTLE_RATES': {
'anon': '100/hour',
'user': '1000/hour'
}
}
混合推荐策略:
特征工程增强:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
def extract_job_features(job_descriptions):
vectorizer = TfidfVectorizer(max_features=500)
tfidf_matrix = vectorizer.fit_transform(job_descriptions)
return tfidf_matrix
智能简历解析:
面试安排系统:
薪酬分析报告:
缓存策略优化:
python复制# 使用Django缓存框架
from django.core.cache import cache
def get_recommendations(user_id):
cache_key = f"user_{user_id}_recommendations"
recommendations = cache.get(cache_key)
if not recommendations:
recommendations = generate_recommendations(user_id)
cache.set(cache_key, recommendations, timeout=3600) # 1小时缓存
return recommendations
异步任务处理:
python复制# tasks.py
from celery import shared_task
@shared_task
def process_resume_async(resume_id):
from resumes.models import Resume
resume = Resume.objects.get(id=resume_id)
# 执行耗时的简历处理逻辑
extract_skills_from_resume(resume)
generate_recommendations(resume.user_id)
技术选型论证部分:
系统设计部分:
测试与分析部分:
演示重点准备:
技术难点阐述:
常见问题准备:
完整的毕业设计文档应包含:
个人经验分享:
在实际指导学生过程中,我发现很多同学在文档写作时容易犯以下错误:
- 需求分析过于笼统,没有具体指标
- 设计文档缺少图表说明
- 测试部分只有功能测试,缺少性能测试
建议同学们按照软件工程的标准流程来组织文档,每个阶段都要有明确的产出物。