init
This commit is contained in:
250
api/tasks.py
Normal file
250
api/tasks.py
Normal file
@@ -0,0 +1,250 @@
|
||||
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')
|
||||
})
|
||||
Reference in New Issue
Block a user