校园兼职市场一直存在信息不对称、管理混乱的问题。学生们经常通过微信群、QQ群或校园公告栏获取兼职信息,这种方式效率低下且存在安全隐患。而商家发布兼职需求也缺乏统一渠道,经常面临招不到合适人选的困境。
这个系统正是为了解决这些痛点而设计。它需要实现以下核心功能:
选择Vue.js作为前端框架主要基于以下考虑:
提示:实际开发中建议使用Vue CLI脚手架初始化项目,它能自动配置Webpack、Babel等工具链,省去大量配置时间。
项目标题中同时提到了Flask和Django,这里需要做出选择:
Flask优势:
Django优势:
考虑到这是一个校园项目,且需要快速迭代,最终选择了Flask作为后端框架。但保留了Django ORM作为数据库访问层,因为:
PyCharm Professional版是Python全栈开发的绝佳选择:
python复制# models.py
from django.db import models
class User(models.Model):
USER_TYPE = (
('student', '学生'),
('business', '商家'),
('admin', '管理员')
)
username = models.CharField(max_length=50, unique=True)
password = models.CharField(max_length=100)
user_type = models.CharField(max_length=10, choices=USER_TYPE)
# 其他公共字段...
class StudentProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
real_name = models.CharField(max_length=20)
student_id = models.CharField(max_length=20)
college = models.CharField(max_length=50)
# 其他学生特有字段...
class Job(models.Model):
STATUS = (
('pending', '待审核'),
('published', '已发布'),
('closed', '已结束')
)
title = models.CharField(max_length=100)
description = models.TextField()
salary = models.DecimalField(max_digits=8, decimal_places=2)
location = models.CharField(max_length=100)
start_time = models.DateTimeField()
end_time = models.DateTimeField()
status = models.CharField(max_length=10, choices=STATUS, default='pending')
publisher = models.ForeignKey(User, on_delete=models.CASCADE)
# 其他职位字段...
class Application(models.Model):
STATUS = (
('applied', '已申请'),
('accepted', '已录用'),
('rejected', '已拒绝')
)
job = models.ForeignKey(Job, on_delete=models.CASCADE)
applicant = models.ForeignKey(User, on_delete=models.CASCADE)
apply_time = models.DateTimeField(auto_now_add=True)
status = models.CharField(max_length=10, choices=STATUS, default='applied')
# 其他申请信息...
python复制# auth.py
from flask import request, jsonify
from functools import wraps
import jwt
from datetime import datetime, timedelta
SECRET_KEY = 'your-secret-key-here'
def generate_token(user_id):
payload = {
'exp': datetime.utcnow() + timedelta(days=1),
'iat': datetime.utcnow(),
'sub': user_id
}
return jwt.encode(payload, SECRET_KEY, algorithm='HS256')
def token_required(f):
@wraps(f)
def decorated(*args, **kwargs):
token = request.headers.get('Authorization')
if not token:
return jsonify({'message': 'Token is missing'}), 403
try:
data = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])
current_user = User.objects.get(id=data['sub'])
except:
return jsonify({'message': 'Token is invalid'}), 403
return f(current_user, *args, **kwargs)
return decorated
前端Vue组件示例:
vue复制<template>
<div class="job-list">
<el-table :data="jobs" style="width: 100%">
<el-table-column prop="title" label="职位名称"></el-table-column>
<el-table-column prop="salary" label="薪资"></el-table-column>
<el-table-column prop="location" label="工作地点"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button
size="mini"
@click="handleApply(scope.row)"
:disabled="scope.row.status !== 'published'"
>申请</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
export default {
data() {
return {
jobs: []
}
},
async created() {
const res = await this.$http.get('/api/jobs')
this.jobs = res.data
},
methods: {
async handleApply(job) {
try {
await this.$http.post(`/api/jobs/${job.id}/apply`)
this.$message.success('申请成功')
} catch (err) {
this.$message.error(err.response.data.message)
}
}
}
}
</script>
后端API实现:
python复制# views.py
from flask import Blueprint, request, jsonify
from .auth import token_required
from .models import Job, Application
job_bp = Blueprint('job', __name__)
@job_bp.route('/jobs', methods=['GET'])
def list_jobs():
jobs = Job.objects.filter(status='published').all()
return jsonify([{
'id': str(job.id),
'title': job.title,
'salary': float(job.salary),
'location': job.location,
'status': job.status
} for job in jobs])
@job_bp.route('/jobs/<job_id>/apply', methods=['POST'])
@token_required
def apply_job(current_user, job_id):
if current_user.user_type != 'student':
return jsonify({'message': '只有学生可以申请兼职'}), 403
job = Job.objects.get_or_404(id=job_id)
if job.status != 'published':
return jsonify({'message': '该职位不可申请'}), 400
existing = Application.objects.filter(
job=job,
applicant=current_user
).first()
if existing:
return jsonify({'message': '您已经申请过该职位'}), 400
Application(
job=job,
applicant=current_user,
status='applied'
).save()
return jsonify({'message': '申请成功'})
bash复制python -m venv venv
source venv/bin/activate # Linux/Mac
venv\Scripts\activate # Windows
bash复制pip install flask django djongo # 使用Django ORM需要djongo
npm install -g @vue/cli
推荐使用Docker Compose部署:
yaml复制version: '3'
services:
web:
build: .
ports:
- "5000:5000"
environment:
- FLASK_ENV=production
- DATABASE_URL=postgres://user:pass@db:5432/job_system
depends_on:
- db
db:
image: postgres:13
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pass
- POSTGRES_DB=job_system
volumes:
- pgdata:/var/lib/postgresql/data
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./frontend/dist:/usr/share/nginx/html
volumes:
pgdata:
在Flask中配置CORS:
python复制from flask_cors import CORS
app = Flask(__name__)
CORS(app, resources={
r"/api/*": {
"origins": ["http://localhost:8080", "https://your-domain.com"],
"methods": ["GET", "POST", "PUT", "DELETE"],
"allow_headers": ["Authorization", "Content-Type"]
}
})
select_related和prefetch_related减少查询次数注意:在实际开发中,建议采用迭代式开发,先实现核心功能,再逐步添加扩展功能。同时要重视测试工作,特别是用户认证和支付相关功能。