feat: 任务批量创建
This commit is contained in:
62
api/tasks.py
62
api/tasks.py
@@ -101,6 +101,68 @@ def create_task(group_id):
|
|||||||
return jsonify({'message': '任务创建成功', 'task_id': task.id}), 201
|
return jsonify({'message': '任务创建成功', 'task_id': task.id}), 201
|
||||||
|
|
||||||
|
|
||||||
|
# 批量创建任务(管理员)
|
||||||
|
@tasks_bp.route('/groups/<int:group_id>/tasks/batch', methods=['POST'])
|
||||||
|
@admin_required
|
||||||
|
def batch_create_tasks(group_id):
|
||||||
|
data = request.json
|
||||||
|
tasks_data = data.get('tasks', [])
|
||||||
|
|
||||||
|
if not tasks_data:
|
||||||
|
return jsonify({'error': '任务列表不能为空'}), 400
|
||||||
|
|
||||||
|
user_id = get_jwt_identity()
|
||||||
|
created_tasks = []
|
||||||
|
errors = []
|
||||||
|
|
||||||
|
for idx, task_data in enumerate(tasks_data):
|
||||||
|
try:
|
||||||
|
if not task_data.get('series_name'):
|
||||||
|
errors.append(f'第{idx+1}行:剧集名称不能为空')
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not task_data.get('series_date'):
|
||||||
|
errors.append(f'第{idx+1}行:日期不能为空')
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
series_date = datetime.strptime(task_data['series_date'], '%Y%m%d').date()
|
||||||
|
except:
|
||||||
|
errors.append(f'第{idx+1}行:日期格式错误')
|
||||||
|
continue
|
||||||
|
|
||||||
|
task = Task(
|
||||||
|
group_id=group_id,
|
||||||
|
series_name=task_data['series_name'],
|
||||||
|
series_date=series_date,
|
||||||
|
priority=task_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)
|
||||||
|
created_tasks.append(task.id)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
errors.append(f'第{idx+1}行:{str(e)}')
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
'message': f'成功创建{len(created_tasks)}个任务',
|
||||||
|
'created': len(created_tasks),
|
||||||
|
'errors': errors
|
||||||
|
}), 201
|
||||||
|
|
||||||
|
|
||||||
# 认领任务
|
# 认领任务
|
||||||
@tasks_bp.route('/tasks/<int:task_id>/claim', methods=['POST'])
|
@tasks_bp.route('/tasks/<int:task_id>/claim', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
|
|||||||
@@ -8,38 +8,59 @@
|
|||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-md-8">
|
<div class="col-md-8">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h4>创建新任务</h4>
|
<h4>创建新任务</h4>
|
||||||
|
<div class="btn-group" role="group">
|
||||||
|
<input type="radio" class="btn-check" name="mode" id="mode-single" value="single" checked>
|
||||||
|
<label class="btn btn-outline-primary" for="mode-single">单个创建</label>
|
||||||
|
<input type="radio" class="btn-check" name="mode" id="mode-batch" value="batch">
|
||||||
|
<label class="btn btn-outline-primary" for="mode-batch">批量创建</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form id="create-task-form">
|
<form id="create-task-form">
|
||||||
<div class="mb-3">
|
<!-- 单个创建模式 -->
|
||||||
<label for="series-name" class="form-label">剧集名称 <span class="text-danger">*</span></label>
|
<div id="single-mode">
|
||||||
<input type="text" class="form-control" id="series-name" required
|
<div class="mb-3">
|
||||||
placeholder="例如:某某剧集 S01E01">
|
<label for="series-name" class="form-label">剧集名称 <span class="text-danger">*</span></label>
|
||||||
|
<input type="text" class="form-control" id="series-name"
|
||||||
|
placeholder="例如:某某剧集 S01E01">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="series-link" class="form-label">剧集链接(可选)</label>
|
||||||
|
<input type="url" class="form-control" id="series-link"
|
||||||
|
placeholder="https://...">
|
||||||
|
<small class="form-text text-muted">资源来源链接</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="series-date" class="form-label">剧集更新日期 <span class="text-danger">*</span></label>
|
||||||
|
<input type="date" class="form-control" id="series-date">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="priority" class="form-label">优先级</label>
|
||||||
|
<select class="form-select" id="priority">
|
||||||
|
<option value="中" selected>中</option>
|
||||||
|
<option value="高">高</option>
|
||||||
|
<option value="低">低</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<!-- 批量创建模式 -->
|
||||||
<label for="series-link" class="form-label">剧集链接(可选)</label>
|
<div id="batch-mode" style="display:none;">
|
||||||
<input type="url" class="form-control" id="series-link"
|
<div class="mb-3">
|
||||||
placeholder="https://...">
|
<label for="batch-input" class="form-label">批量输入 <span class="text-danger">*</span></label>
|
||||||
<small class="form-text text-muted">资源来源链接</small>
|
<textarea class="form-control" id="batch-input" rows="10"
|
||||||
|
placeholder="每行一个任务,格式:剧集名称,日期,优先级 例如: 剧集1,20251209,高 剧集2,20251209,中 剧集3,20251209,低"></textarea>
|
||||||
|
<small class="form-text text-muted">
|
||||||
|
格式说明:剧集名称,日期(YYYYMMDD),优先级(高/中/低)
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="series-date" class="form-label">剧集更新日期 <span class="text-danger">*</span></label>
|
|
||||||
<input type="date" class="form-control" id="series-date" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label for="priority" class="form-label">优先级</label>
|
|
||||||
<select class="form-select" id="priority">
|
|
||||||
<option value="中" selected>中</option>
|
|
||||||
<option value="高">高</option>
|
|
||||||
<option value="低">低</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
<a href="/tasks" class="btn btn-secondary">取消</a>
|
<a href="/tasks" class="btn btn-secondary">取消</a>
|
||||||
<button type="submit" class="btn btn-primary">创建任务</button>
|
<button type="submit" class="btn btn-primary">创建任务</button>
|
||||||
@@ -57,35 +78,112 @@
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
// 设置默认日期为今天
|
// 设置默认日期为今天
|
||||||
$('#series-date').val(new Date().toISOString().split('T')[0]);
|
$('#series-date').val(new Date().toISOString().split('T')[0]);
|
||||||
|
|
||||||
|
// 模式切换
|
||||||
|
$('input[name="mode"]').change(function() {
|
||||||
|
if ($(this).val() === 'single') {
|
||||||
|
$('#single-mode').show();
|
||||||
|
$('#batch-mode').hide();
|
||||||
|
} else {
|
||||||
|
$('#single-mode').hide();
|
||||||
|
$('#batch-mode').show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#create-task-form').submit(function(e) {
|
$('#create-task-form').submit(function(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const data = {
|
const mode = $('input[name="mode"]:checked').val();
|
||||||
series_name: $('#series-name').val(),
|
|
||||||
series_link: $('#series-link').val() || null,
|
if (mode === 'single') {
|
||||||
series_date: $('#series-date').val(),
|
createSingleTask();
|
||||||
priority: $('#priority').val()
|
} else {
|
||||||
};
|
createBatchTasks();
|
||||||
|
}
|
||||||
$.ajax({
|
|
||||||
url: '/api/groups/1/tasks',
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Authorization': 'Bearer ' + localStorage.getItem('access_token'),
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
data: JSON.stringify(data),
|
|
||||||
success: function(response) {
|
|
||||||
alert('任务创建成功!');
|
|
||||||
window.location.href = '/tasks';
|
|
||||||
},
|
|
||||||
error: function(xhr) {
|
|
||||||
const error = xhr.responseJSON?.error || '创建失败';
|
|
||||||
alert(error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function createSingleTask() {
|
||||||
|
const data = {
|
||||||
|
series_name: $('#series-name').val(),
|
||||||
|
series_link: $('#series-link').val() || null,
|
||||||
|
series_date: $('#series-date').val(),
|
||||||
|
priority: $('#priority').val()
|
||||||
|
};
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/groups/1/tasks',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('access_token'),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
data: JSON.stringify(data),
|
||||||
|
success: function(response) {
|
||||||
|
alert('任务创建成功!');
|
||||||
|
window.location.href = '/tasks';
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
const error = xhr.responseJSON?.error || '创建失败';
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBatchTasks() {
|
||||||
|
const input = $('#batch-input').val().trim();
|
||||||
|
|
||||||
|
if (!input) {
|
||||||
|
alert('请输入任务数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = input.split('\n');
|
||||||
|
const tasks = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
const line = lines[i].trim();
|
||||||
|
if (!line) continue;
|
||||||
|
|
||||||
|
const parts = line.split(',');
|
||||||
|
if (parts.length !== 3) {
|
||||||
|
alert(`第${i+1}行格式错误,应为:剧集名称,日期,优先级`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.push({
|
||||||
|
series_name: parts[0].trim(),
|
||||||
|
series_date: parts[1].trim(),
|
||||||
|
priority: parts[2].trim()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tasks.length === 0) {
|
||||||
|
alert('没有有效的任务数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: '/api/groups/1/tasks/batch',
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('access_token'),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
data: JSON.stringify({ tasks: tasks }),
|
||||||
|
success: function(response) {
|
||||||
|
let message = response.message;
|
||||||
|
if (response.errors && response.errors.length > 0) {
|
||||||
|
message += '\n\n错误信息:\n' + response.errors.join('\n');
|
||||||
|
}
|
||||||
|
alert(message);
|
||||||
|
window.location.href = '/tasks';
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
const error = xhr.responseJSON?.error || '批量创建失败';
|
||||||
|
alert(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user