from flask import Blueprint, request, jsonify from flask_jwt_extended import get_jwt_identity from models import db, Task, TaskLog, Group, User from auth import login_required, admin_required, group_member_required, get_current_user from datetime import datetime from sqlalchemy import func, and_ tasks_bp = Blueprint('tasks', __name__) # 获取任务列表 @tasks_bp.route('/groups//tasks', methods=['GET']) @login_required def get_tasks(group_id): status = request.args.get('status') # pending/claimed/completed user_id = request.args.get('user_id', type=int) page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 20, type=int) query = Task.query.filter_by(group_id=group_id) if status: query = query.filter_by(status=status) if user_id: query = query.filter_by(claimed_by=user_id) query = query.order_by(Task.series_date.desc(), Task.created_at.desc()) pagination = query.paginate(page=page, per_page=per_page, error_out=False) tasks = [] for task in pagination.items: tasks.append({ 'id': task.id, 'series_name': task.series_name, 'series_link': task.series_link, 'series_date': task.series_date.strftime('%Y-%m-%d') if task.series_date else None, 'priority': task.priority, 'status': task.status, 'claimed_by': task.claimer.username if task.claimer else None, 'claimed_by_id': task.claimed_by, 'claimed_at': task.claimed_at.strftime('%Y-%m-%d %H:%M') if task.claimed_at else None, 'claim_note': task.claim_note, 'torrent_id': task.torrent_id, 'complete_note': task.complete_note, 'completed_at': task.completed_at.strftime('%Y-%m-%d %H:%M') if task.completed_at else None, 'creator': task.creator.username, 'created_at': task.created_at.strftime('%Y-%m-%d %H:%M') }) return jsonify({ 'tasks': tasks, 'total': pagination.total, 'page': page, 'pages': pagination.pages }) # 创建任务(管理员) @tasks_bp.route('/groups//tasks', methods=['POST']) @admin_required def create_task(group_id): data = request.json # 验证必填字段 if not data.get('series_name'): return jsonify({'error': '剧集名称不能为空'}), 400 if not data.get('series_date'): return jsonify({'error': '剧集更新日期不能为空'}), 400 try: series_date = datetime.strptime(data['series_date'], '%Y-%m-%d').date() except: return jsonify({'error': '日期格式错误'}), 400 user_id = get_jwt_identity() task = Task( group_id=group_id, series_name=data['series_name'], series_link=data.get('series_link'), series_date=series_date, priority=data.get('priority', '中'), created_by=user_id ) db.session.add(task) db.session.flush() # 记录日志 log = TaskLog( task_id=task.id, user_id=user_id, action='create', comment=f'创建任务:{task.series_name}' ) db.session.add(log) db.session.commit() return jsonify({'message': '任务创建成功', 'task_id': task.id}), 201 # 认领任务 @tasks_bp.route('/tasks//claim', methods=['POST']) @login_required def claim_task(task_id): task = db.session.get(Task, task_id) if not task: return jsonify({'error': '任务不存在'}), 404 if task.status != 'pending': return jsonify({'error': '该任务已被认领或已完成'}), 400 user_id = get_jwt_identity() data = request.json or {} # 使用数据库锁防止并发认领 task = db.session.query(Task).filter_by(id=task_id, status='pending').with_for_update().first() if not task: return jsonify({'error': '任务已被他人认领'}), 400 task.status = 'claimed' task.claimed_by = user_id task.claimed_at = datetime.utcnow() task.claim_note = data.get('claim_note') log = TaskLog( task_id=task_id, user_id=user_id, action='claim', comment=data.get('claim_note') ) db.session.add(log) db.session.commit() return jsonify({'message': '认领成功'}) # 完成任务 # 完成任务 @tasks_bp.route('/tasks//complete', methods=['POST']) @login_required def complete_task(task_id): task = db.session.get(Task, task_id) if not task: return jsonify({'error': '任务不存在'}), 404 user_id = get_jwt_identity() # 确保类型一致,转换为整数比较 if task.claimed_by != int(user_id): return jsonify({'error': f'只能完成自己认领的任务 (任务认领者: {task.claimed_by}, 当前用户: {user_id})'}), 403 if task.status != 'claimed': return jsonify({'error': '任务状态错误'}), 400 data = request.json if not data.get('torrent_id'): return jsonify({'error': '种子ID不能为空'}), 400 task.status = 'completed' task.torrent_id = data['torrent_id'] task.complete_note = data.get('complete_note') task.completed_at = datetime.utcnow() log = TaskLog( task_id=task_id, user_id=int(user_id), action='complete', comment=f'种子ID: {data["torrent_id"]}' ) db.session.add(log) db.session.commit() return jsonify({'message': '任务完成'}) # 取消认领(用户自己) @tasks_bp.route('/tasks//cancel-claim', methods=['POST']) @login_required def cancel_claim(task_id): task = db.session.get(Task, task_id) if not task: return jsonify({'error': '任务不存在'}), 404 user_id = get_jwt_identity() if task.claimed_by != user_id: return jsonify({'error': '只能取消自己认领的任务'}), 403 if task.status != 'claimed': return jsonify({'error': '任务状态错误'}), 400 task.status = 'pending' task.claimed_by = None task.claimed_at = None task.claim_note = None log = TaskLog( task_id=task_id, user_id=user_id, action='cancel_claim', comment='取消认领' ) db.session.add(log) db.session.commit() return jsonify({'message': '已取消认领'}) # 删除任务(管理员) @tasks_bp.route('/tasks/', methods=['DELETE']) @admin_required def delete_task(task_id): task = db.session.get(Task, task_id) if not task: return jsonify({'error': '任务不存在'}), 404 db.session.delete(task) db.session.commit() return jsonify({'message': '任务已删除'}) # 获取任务详情 @tasks_bp.route('/tasks/', methods=['GET']) @login_required def get_task(task_id): task = db.session.get(Task, task_id) if not task: return jsonify({'error': '任务不存在'}), 404 return jsonify({ 'id': task.id, 'series_name': task.series_name, 'series_link': task.series_link, 'series_date': task.series_date.strftime('%Y-%m-%d'), 'priority': task.priority, 'status': task.status, 'claimed_by': task.claimer.username if task.claimer else None, 'claimed_at': task.claimed_at.strftime('%Y-%m-%d %H:%M') if task.claimed_at else None, 'claim_note': task.claim_note, 'torrent_id': task.torrent_id, 'complete_note': task.complete_note, 'completed_at': task.completed_at.strftime('%Y-%m-%d %H:%M') if task.completed_at else None, 'creator': task.creator.username, 'created_at': task.created_at.strftime('%Y-%m-%d %H:%M') })