mobile app database models admin_contests admin_dashboard admin_exams admin_posts admin_teacher_applications admin_users apply_contest apply_teacher base chat contest_detail contest_list exam_result forum notifications profile themes
This commit is contained in:
85
app.py
85
app.py
@@ -554,6 +554,16 @@ def exam_list():
|
||||
subject_filter = request.args.get('subject', '').strip()
|
||||
|
||||
query = Exam.query
|
||||
|
||||
# 只显示公开考试或用户自己创建的私有考试
|
||||
from sqlalchemy import or_
|
||||
query = query.filter(
|
||||
or_(
|
||||
Exam.visibility == 'public',
|
||||
Exam.creator_id == user.get('id')
|
||||
)
|
||||
)
|
||||
|
||||
if subject_filter:
|
||||
query = query.filter_by(subject=subject_filter)
|
||||
if search_query:
|
||||
@@ -1059,6 +1069,23 @@ def apply_contest():
|
||||
flash('满分分数必须大于0')
|
||||
return redirect(url_for('apply_contest'))
|
||||
|
||||
# 处理图片上传
|
||||
image_url = None
|
||||
if 'image' in request.files:
|
||||
file = request.files['image']
|
||||
if file and file.filename:
|
||||
if allowed_file(file.filename):
|
||||
filename = secure_filename(file.filename)
|
||||
# 生成唯一文件名
|
||||
ext = filename.rsplit('.', 1)[1].lower()
|
||||
unique_filename = f"contest_{user_id}_{int(time.time())}.{ext}"
|
||||
filepath = os.path.join(UPLOAD_FOLDER, unique_filename)
|
||||
file.save(filepath)
|
||||
image_url = f'/static/uploads/{unique_filename}'
|
||||
else:
|
||||
flash('不支持的图片格式,请上传 JPG、PNG 或 GIF 格式的图片')
|
||||
return redirect(url_for('apply_contest'))
|
||||
|
||||
# 如果存在申请,更新它;否则创建新申请
|
||||
if existing:
|
||||
# 更新现有申请
|
||||
@@ -1073,6 +1100,8 @@ def apply_contest():
|
||||
existing.responsible_phone = responsible_phone
|
||||
existing.responsible_email = responsible_email
|
||||
existing.organization = organization
|
||||
if image_url:
|
||||
existing.image = image_url
|
||||
existing.status = 'pending'
|
||||
existing.applied_at = datetime.utcnow()
|
||||
existing.reviewed_at = None
|
||||
@@ -1092,7 +1121,8 @@ def apply_contest():
|
||||
responsible_person=responsible_person,
|
||||
responsible_phone=responsible_phone,
|
||||
responsible_email=responsible_email,
|
||||
organization=organization
|
||||
organization=organization,
|
||||
image=image_url
|
||||
)
|
||||
db.session.add(app)
|
||||
flash_msg = '申请已提交,请等待管理员审核'
|
||||
@@ -2501,20 +2531,26 @@ def api_create_exam():
|
||||
return jsonify({'success': False, 'message': '请先登录'}), 401
|
||||
data = request.get_json(force=True, silent=True)
|
||||
contest_id = data.get('contest_id')
|
||||
visibility = data.get('visibility', 'private') # 默认私有
|
||||
|
||||
# 杯赛考试:只有杯赛负责人或系统管理员可以创建
|
||||
# 杯赛考试:只有杯赛负责人、老师或系统管理员可以创建
|
||||
if contest_id:
|
||||
contest = Contest.query.get(contest_id)
|
||||
if not contest or contest.status == 'abolished':
|
||||
return jsonify({'success': False, 'message': '杯赛不存在或已废止'}), 400
|
||||
membership = ContestMembership.query.filter_by(
|
||||
user_id=user['id'], contest_id=contest_id, role='owner').first()
|
||||
user_id=user['id'], contest_id=contest_id).first()
|
||||
# 检查是否是负责人、老师或管理员
|
||||
if not membership and user.get('role') != 'admin':
|
||||
return jsonify({'success': False, 'message': '只有杯赛负责人才能组织考试'}), 403
|
||||
return jsonify({'success': False, 'message': '只有杯赛成员才能创建杯赛考试'}), 403
|
||||
# 杯赛考试默认为公开(杯赛内可见)
|
||||
if visibility == 'private':
|
||||
visibility = 'public'
|
||||
else:
|
||||
# 普通考试:需要 teacher 或 admin 角色
|
||||
if user.get('role') not in ('teacher', 'admin'):
|
||||
return jsonify({'success': False, 'message': '无权限'}), 403
|
||||
# 普通考试:所有登录用户都可以创建私有考试
|
||||
# 只有 teacher 或 admin 可以创建公开考试
|
||||
if visibility == 'public' and user.get('role') not in ('teacher', 'admin'):
|
||||
return jsonify({'success': False, 'message': '只有教师和管理员可以创建公开考试'}), 403
|
||||
|
||||
title = data.get('title', '')
|
||||
subject = data.get('subject', '')
|
||||
@@ -2532,7 +2568,8 @@ def api_create_exam():
|
||||
duration=duration,
|
||||
total_score=total_score,
|
||||
creator_id=user.get('id'),
|
||||
contest_id=contest_id
|
||||
contest_id=contest_id,
|
||||
visibility=visibility
|
||||
)
|
||||
# 解析预定时间
|
||||
if scheduled_start:
|
||||
@@ -2708,6 +2745,30 @@ def api_update_exam_status(exam_id):
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': f'试卷已{"发布" if new_status == "available" else "关闭"}'})
|
||||
|
||||
@app.route('/api/exams/<int:exam_id>/visibility', methods=['POST'])
|
||||
@login_required
|
||||
def api_update_exam_visibility(exam_id):
|
||||
user = session.get('user')
|
||||
exam = Exam.query.get(exam_id)
|
||||
if not exam:
|
||||
return jsonify({'success': False, 'message': '试卷不存在'}), 404
|
||||
if exam.creator_id != user.get('id'):
|
||||
return jsonify({'success': False, 'message': '只能修改自己创建的试卷'}), 403
|
||||
|
||||
data = request.get_json(force=True, silent=True)
|
||||
new_visibility = data.get('visibility', '')
|
||||
|
||||
if new_visibility not in ('private', 'public'):
|
||||
return jsonify({'success': False, 'message': '无效的可见性设置'}), 400
|
||||
|
||||
# 检查权限:只有教师和管理员可以设置为公开
|
||||
if new_visibility == 'public' and user.get('role') not in ('teacher', 'admin'):
|
||||
return jsonify({'success': False, 'message': '只有教师和管理员可以创建公开考试'}), 403
|
||||
|
||||
exam.visibility = new_visibility
|
||||
db.session.commit()
|
||||
return jsonify({'success': True, 'message': f'试卷已设置为{"公开" if new_visibility == "public" else "私有"}'})
|
||||
|
||||
@app.route('/api/exams/<int:exam_id>', methods=['DELETE'])
|
||||
def api_delete_exam(exam_id):
|
||||
user = session.get('user')
|
||||
@@ -3229,12 +3290,20 @@ def api_search_posts():
|
||||
uid = user.get('id') if user else None
|
||||
data = []
|
||||
for p in posts:
|
||||
# 计算作者等级
|
||||
author_post_count = Post.query.filter_by(author_id=p.author_id).count()
|
||||
author_reply_count = Reply.query.filter_by(author_id=p.author_id).count()
|
||||
author_likes = db.session.query(db.func.sum(Post.likes)).filter_by(author_id=p.author_id).scalar() or 0
|
||||
author_points = author_post_count * 10 + author_reply_count * 3 + author_likes * 2
|
||||
author_level = calc_level(author_points)
|
||||
|
||||
p_dict = {
|
||||
'id': p.id,
|
||||
'title': p.title,
|
||||
'content': p.content[:200] + ('...' if len(p.content) > 200 else ''),
|
||||
'author': p.author.name,
|
||||
'author_id': p.author_id,
|
||||
'author_level': author_level,
|
||||
'tag': p.tag,
|
||||
'is_official': p.is_official,
|
||||
'pinned': p.pinned,
|
||||
|
||||
Reference in New Issue
Block a user