Files
PTGroup/api/tasks.py
DengDai 02ecea06f8 init
2025-12-09 13:08:38 +08:00

251 lines
7.7 KiB
Python

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/<int:group_id>/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/<int:group_id>/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/<int:task_id>/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/<int:task_id>/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/<int:task_id>/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/<int:task_id>', 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/<int:task_id>', 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')
})