加入周期任务里面的每个周期的具体时间

This commit is contained in:
liuwei
2025-06-10 16:27:51 +08:00
parent 0e43a7d488
commit 0570328152
3 changed files with 161 additions and 16 deletions

View File

@@ -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' }
] ]

View File

@@ -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'),

View File

@@ -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-60表示周日
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 interval == 'daily': # 解析执行时间
next_time = current_time.replace(day=current_time.day + 1) if recurring_time:
elif interval == 'weekly': try:
next_time = current_time.replace(day=current_time.day + 7) hour, minute = map(int, recurring_time.split(':'))
elif interval == 'monthly': if not (0 <= hour <= 23 and 0 <= minute <= 59):
# 处理月份边界情况 self.LOG.error(f"无效的执行时间格式: {recurring_time}")
if current_time.month == 12: return None
next_time = current_time.replace(year=current_time.year + 1, month=1) except ValueError:
self.LOG.error(f"执行时间格式错误: {recurring_time}")
return None
else: else:
next_time = current_time.replace(month=current_time.month + 1) hour, minute = current_time.hour, current_time.minute
if interval == 'daily':
# 如果是首次执行,使用当前时间
if current_time == datetime.now():
next_time = current_time
else:
# 否则,设置为明天的同一时间
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-60表示周日
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