加入周期任务里面的每个周期的具体时间
This commit is contained in:
@@ -236,6 +236,49 @@
|
|||||||
<el-option label="每月" value="monthly"></el-option>
|
<el-option label="每月" value="monthly"></el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="执行时间"
|
||||||
|
prop="recurring_time"
|
||||||
|
{% raw %}v-if="taskForm.schedule_type === 'recurring'" {% endraw %}>
|
||||||
|
<el-time-picker
|
||||||
|
{% raw %}v-model="taskForm.recurring_time" {% endraw %}
|
||||||
|
format="HH:mm"
|
||||||
|
placeholder="选择时间"
|
||||||
|
value-format="HH:mm">
|
||||||
|
</el-time-picker>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="每周执行日"
|
||||||
|
prop="weekly_days"
|
||||||
|
{% raw %}v-if="taskForm.schedule_type === 'recurring' && taskForm.recurring_interval === 'weekly'" {% endraw %}>
|
||||||
|
<el-select
|
||||||
|
{% raw %}v-model="taskForm.weekly_days" {% endraw %}
|
||||||
|
multiple
|
||||||
|
placeholder="请选择每周执行日">
|
||||||
|
<el-option label="周一" value="1"></el-option>
|
||||||
|
<el-option label="周二" value="2"></el-option>
|
||||||
|
<el-option label="周三" value="3"></el-option>
|
||||||
|
<el-option label="周四" value="4"></el-option>
|
||||||
|
<el-option label="周五" value="5"></el-option>
|
||||||
|
<el-option label="周六" value="6"></el-option>
|
||||||
|
<el-option label="周日" value="0"></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="每月执行日"
|
||||||
|
prop="monthly_day"
|
||||||
|
{% raw %}v-if="taskForm.schedule_type === 'recurring' && taskForm.recurring_interval === 'monthly'" {% endraw %}>
|
||||||
|
<el-select
|
||||||
|
{% raw %}v-model="taskForm.monthly_day" {% endraw %}
|
||||||
|
placeholder="请选择每月执行日">
|
||||||
|
<el-option
|
||||||
|
{% raw %}v-for="day in 31"
|
||||||
|
:key="day"
|
||||||
|
:label="`${day}日`"
|
||||||
|
:value="day" {% endraw %}>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
label="重复结束时间"
|
label="重复结束时间"
|
||||||
prop="recurring_end"
|
prop="recurring_end"
|
||||||
@@ -417,6 +460,21 @@ new Vue({
|
|||||||
schedule_type: [
|
schedule_type: [
|
||||||
{ required: true, message: '请选择任务类型', trigger: 'change' }
|
{ required: true, message: '请选择任务类型', trigger: 'change' }
|
||||||
],
|
],
|
||||||
|
recurring_interval: [
|
||||||
|
{ required: true, message: '请选择重复间隔', trigger: 'change' }
|
||||||
|
],
|
||||||
|
recurring_time: [
|
||||||
|
{ required: true, message: '请选择执行时间', trigger: 'change' }
|
||||||
|
],
|
||||||
|
weekly_days: [
|
||||||
|
{ required: true, message: '请选择每周执行日', trigger: 'change' }
|
||||||
|
],
|
||||||
|
monthly_day: [
|
||||||
|
{ required: true, message: '请选择每月执行日', trigger: 'change' }
|
||||||
|
],
|
||||||
|
recurring_end: [
|
||||||
|
{ required: true, message: '请选择重复结束时间', trigger: 'change' }
|
||||||
|
],
|
||||||
groups: [
|
groups: [
|
||||||
{ required: true, message: '请选择目标群组', trigger: 'change' }
|
{ required: true, message: '请选择目标群组', trigger: 'change' }
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -25,6 +25,9 @@ class TaskDBOperator(BaseDBOperator):
|
|||||||
schedule_time DATETIME NOT NULL,
|
schedule_time DATETIME NOT NULL,
|
||||||
recurring_interval ENUM('daily', 'weekly', 'monthly') DEFAULT NULL,
|
recurring_interval ENUM('daily', 'weekly', 'monthly') DEFAULT NULL,
|
||||||
recurring_end DATETIME DEFAULT NULL,
|
recurring_end DATETIME DEFAULT NULL,
|
||||||
|
recurring_time TIME DEFAULT NULL,
|
||||||
|
weekly_days JSON DEFAULT NULL,
|
||||||
|
monthly_day INT DEFAULT NULL,
|
||||||
content_text TEXT(500),
|
content_text TEXT(500),
|
||||||
content_image VARCHAR(255),
|
content_image VARCHAR(255),
|
||||||
content_link JSON,
|
content_link JSON,
|
||||||
@@ -93,11 +96,11 @@ class TaskDBOperator(BaseDBOperator):
|
|||||||
sql = """
|
sql = """
|
||||||
INSERT INTO t_push_tasks (
|
INSERT INTO t_push_tasks (
|
||||||
task_id, name, schedule_type, schedule_time, recurring_interval,
|
task_id, name, schedule_type, schedule_time, recurring_interval,
|
||||||
recurring_end, content_text, content_image, content_link,
|
recurring_end, recurring_time, weekly_days, monthly_day, content_text,
|
||||||
content_miniprogram, groups, priority, status, creator_id,
|
content_image, content_link, content_miniprogram, groups, priority,
|
||||||
preview_recipients
|
status, creator_id, preview_recipients
|
||||||
) VALUES (
|
) VALUES (
|
||||||
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
|
%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
|
||||||
)
|
)
|
||||||
"""
|
"""
|
||||||
# 将字典和列表类型转换为JSON字符串
|
# 将字典和列表类型转换为JSON字符串
|
||||||
@@ -112,6 +115,9 @@ class TaskDBOperator(BaseDBOperator):
|
|||||||
task_data['schedule_time'],
|
task_data['schedule_time'],
|
||||||
task_data.get('recurring_interval'),
|
task_data.get('recurring_interval'),
|
||||||
task_data.get('recurring_end'),
|
task_data.get('recurring_end'),
|
||||||
|
task_data.get('recurring_time'),
|
||||||
|
task_data.get('weekly_days'),
|
||||||
|
task_data.get('monthly_day'),
|
||||||
task_data.get('content_text'),
|
task_data.get('content_text'),
|
||||||
task_data.get('content_image'),
|
task_data.get('content_image'),
|
||||||
task_data.get('content_link'),
|
task_data.get('content_link'),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
from datetime import datetime
|
from datetime import datetime, timedelta
|
||||||
from typing import Dict, Any, List, Optional, Tuple
|
from typing import Dict, Any, List, Optional, Tuple
|
||||||
|
|
||||||
from loguru import logger
|
from loguru import logger
|
||||||
@@ -224,7 +224,10 @@ class MessagePushTask(MessagePluginInterface):
|
|||||||
next_time = self._calculate_next_schedule_time(
|
next_time = self._calculate_next_schedule_time(
|
||||||
task['schedule_time'],
|
task['schedule_time'],
|
||||||
task['recurring_interval'],
|
task['recurring_interval'],
|
||||||
task['recurring_end']
|
task['recurring_end'],
|
||||||
|
task['recurring_time'],
|
||||||
|
task['weekly_days'],
|
||||||
|
task['monthly_day']
|
||||||
)
|
)
|
||||||
if next_time:
|
if next_time:
|
||||||
self.db.update_task(task['task_id'], {
|
self.db.update_task(task['task_id'], {
|
||||||
@@ -250,30 +253,108 @@ class MessagePushTask(MessagePluginInterface):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.LOG.error(f"处理定时任务出错: {e}")
|
self.LOG.error(f"处理定时任务出错: {e}")
|
||||||
|
|
||||||
def _calculate_next_schedule_time(self, current_time: datetime, interval: str, end_time: datetime = None) -> \
|
def _calculate_next_schedule_time(self, current_time: datetime, interval: str, end_time: datetime = None,
|
||||||
Optional[datetime]:
|
recurring_time: str = None, weekly_days: List[int] = None,
|
||||||
|
monthly_day: int = None) -> Optional[datetime]:
|
||||||
"""计算下次执行时间
|
"""计算下次执行时间
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
current_time: 当前执行时间
|
current_time: 当前执行时间
|
||||||
interval: 重复间隔(daily/weekly/monthly)
|
interval: 重复间隔(daily/weekly/monthly)
|
||||||
end_time: 结束时间
|
end_time: 结束时间
|
||||||
|
recurring_time: 重复执行时间(HH:mm格式)
|
||||||
|
weekly_days: 每周执行日列表(0-6,0表示周日)
|
||||||
|
monthly_day: 每月执行日(1-31)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
下次执行时间,如果已超过结束时间则返回None
|
下次执行时间,如果已超过结束时间则返回None
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
if not end_time or current_time < end_time:
|
if not end_time or current_time < end_time:
|
||||||
|
# 解析执行时间
|
||||||
|
if recurring_time:
|
||||||
|
try:
|
||||||
|
hour, minute = map(int, recurring_time.split(':'))
|
||||||
|
if not (0 <= hour <= 23 and 0 <= minute <= 59):
|
||||||
|
self.LOG.error(f"无效的执行时间格式: {recurring_time}")
|
||||||
|
return None
|
||||||
|
except ValueError:
|
||||||
|
self.LOG.error(f"执行时间格式错误: {recurring_time}")
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
hour, minute = current_time.hour, current_time.minute
|
||||||
|
|
||||||
if interval == 'daily':
|
if interval == 'daily':
|
||||||
next_time = current_time.replace(day=current_time.day + 1)
|
# 如果是首次执行,使用当前时间
|
||||||
elif interval == 'weekly':
|
if current_time == datetime.now():
|
||||||
next_time = current_time.replace(day=current_time.day + 7)
|
next_time = current_time
|
||||||
elif interval == 'monthly':
|
|
||||||
# 处理月份边界情况
|
|
||||||
if current_time.month == 12:
|
|
||||||
next_time = current_time.replace(year=current_time.year + 1, month=1)
|
|
||||||
else:
|
else:
|
||||||
next_time = current_time.replace(month=current_time.month + 1)
|
# 否则,设置为明天的同一时间
|
||||||
|
next_time = current_time + timedelta(days=1)
|
||||||
|
next_time = next_time.replace(hour=hour, minute=minute)
|
||||||
|
|
||||||
|
elif interval == 'weekly' and weekly_days:
|
||||||
|
# 验证每周执行日
|
||||||
|
if not all(0 <= day <= 6 for day in weekly_days):
|
||||||
|
self.LOG.error(f"无效的每周执行日: {weekly_days}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取当前是周几(0-6,0表示周日)
|
||||||
|
current_weekday = current_time.weekday()
|
||||||
|
# 找到下一个执行日
|
||||||
|
next_weekday = None
|
||||||
|
for day in sorted(weekly_days):
|
||||||
|
if day > current_weekday:
|
||||||
|
next_weekday = day
|
||||||
|
break
|
||||||
|
if next_weekday is None:
|
||||||
|
# 如果当前是本周最后一个执行日,则从下周第一个执行日开始
|
||||||
|
next_weekday = min(weekly_days)
|
||||||
|
days_ahead = 7 - current_weekday + next_weekday
|
||||||
|
else:
|
||||||
|
days_ahead = next_weekday - current_weekday
|
||||||
|
|
||||||
|
next_time = current_time + timedelta(days=days_ahead)
|
||||||
|
next_time = next_time.replace(hour=hour, minute=minute)
|
||||||
|
|
||||||
|
elif interval == 'monthly' and monthly_day:
|
||||||
|
# 验证每月执行日
|
||||||
|
if not (1 <= monthly_day <= 31):
|
||||||
|
self.LOG.error(f"无效的每月执行日: {monthly_day}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 获取当前日期
|
||||||
|
current_day = current_time.day
|
||||||
|
# 计算下一个执行日期
|
||||||
|
if current_day < monthly_day:
|
||||||
|
# 如果当前日期小于执行日期,则在本月执行
|
||||||
|
try:
|
||||||
|
next_time = current_time.replace(day=monthly_day)
|
||||||
|
except ValueError:
|
||||||
|
# 处理无效日期(如2月30日)
|
||||||
|
if current_time.month == 12:
|
||||||
|
next_time = current_time.replace(year=current_time.year + 1, month=1, day=1)
|
||||||
|
else:
|
||||||
|
next_time = current_time.replace(month=current_time.month + 1, day=1)
|
||||||
|
else:
|
||||||
|
# 否则在下个月执行
|
||||||
|
if current_time.month == 12:
|
||||||
|
next_time = current_time.replace(year=current_time.year + 1, month=1, day=1)
|
||||||
|
else:
|
||||||
|
next_time = current_time.replace(month=current_time.month + 1, day=1)
|
||||||
|
|
||||||
|
# 尝试设置到指定的日期
|
||||||
|
try:
|
||||||
|
next_time = next_time.replace(day=monthly_day)
|
||||||
|
except ValueError:
|
||||||
|
# 如果下个月的指定日期无效,则使用下个月的最后一天
|
||||||
|
if next_time.month == 12:
|
||||||
|
next_time = next_time.replace(year=next_time.year + 1, month=1, day=1)
|
||||||
|
else:
|
||||||
|
next_time = next_time.replace(month=next_time.month + 1, day=1)
|
||||||
|
next_time = next_time - timedelta(days=1)
|
||||||
|
|
||||||
|
next_time = next_time.replace(hour=hour, minute=minute)
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user