1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-13 17:24:44 +08:00

feat: 计划任务增加每隔 n 秒执行 (#1132)

This commit is contained in:
ssongliu 2023-05-24 18:36:41 +08:00 committed by GitHub
parent 93b03c7212
commit f65f0d86aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 77 additions and 22 deletions

View File

@ -10,6 +10,7 @@ type CronjobCreate struct {
Day int `json:"day" validate:"number"`
Hour int `json:"hour" validate:"number"`
Minute int `json:"minute" validate:"number"`
Second int `json:"second" validate:"number"`
Script string `json:"script"`
Website string `json:"website"`
@ -30,6 +31,7 @@ type CronjobUpdate struct {
Day int `json:"day" validate:"number"`
Hour int `json:"hour" validate:"number"`
Minute int `json:"minute" validate:"number"`
Second int `json:"second" validate:"number"`
Script string `json:"script"`
Website string `json:"website"`
@ -71,6 +73,7 @@ type CronjobInfo struct {
Day int `json:"day"`
Hour int `json:"hour"`
Minute int `json:"minute"`
Second int `json:"second"`
Script string `json:"script"`
Website string `json:"website"`

View File

@ -13,6 +13,7 @@ type Cronjob struct {
Day uint64 `gorm:"type:decimal" json:"day"`
Hour uint64 `gorm:"type:decimal" json:"hour"`
Minute uint64 `gorm:"type:decimal" json:"minute"`
Second uint64 `gorm:"type:decimal" json:"second"`
Script string `gorm:"longtext" json:"script"`
Website string `gorm:"type:varchar(64)" json:"website"`

View File

@ -322,6 +322,8 @@ func loadSpec(cronjob model.Cronjob) string {
return fmt.Sprintf("%v * * * *", cronjob.Minute)
case "perNMinute":
return fmt.Sprintf("@every %vm", cronjob.Minute)
case "perNSecond":
return fmt.Sprintf("@every %vs", cronjob.Second)
default:
return ""
}

View File

@ -29,6 +29,7 @@ func Init() {
migrations.UpdateTableSetting,
migrations.UpdateTableAppDetail,
migrations.AddBindAndAllowIPs,
migrations.UpdateCronjobWithSecond,
})
if err := m.Migrate(); err != nil {
global.LOG.Error(err)

View File

@ -337,7 +337,7 @@ var UpdateTableAppDetail = &gormigrate.Migration{
}
var AddBindAndAllowIPs = &gormigrate.Migration{
ID: "20230414-add-bind-and-allow",
ID: "20230517-add-bind-and-allow",
Migrate: func(tx *gorm.DB) error {
if err := tx.Create(&model.Setting{Key: "BindDomain", Value: ""}).Error; err != nil {
return err
@ -354,3 +354,13 @@ var AddBindAndAllowIPs = &gormigrate.Migration{
return nil
},
}
var UpdateCronjobWithSecond = &gormigrate.Migration{
ID: "20200524-update-table-cronjob",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.Cronjob{}); err != nil {
return err
}
return nil
},
}

View File

@ -10,6 +10,7 @@ export namespace Cronjob {
day: number;
hour: number;
minute: number;
second: number;
script: string;
website: string;
@ -31,6 +32,7 @@ export namespace Cronjob {
day: number;
hour: number;
minute: number;
second: number;
script: string;
website: string;
@ -49,6 +51,7 @@ export namespace Cronjob {
day: number;
hour: number;
minute: number;
second: number;
script: string;
website: string;

View File

@ -651,12 +651,14 @@ const message = {
perDay: 'Every days',
perNHour: 'Every N hours',
perNMinute: 'Every N minutes',
perNSecond: 'Every N seconds',
per: 'Every ',
handle: 'Handle',
day: 'Day',
day1: 'Day',
hour: ' Hour',
minute: ' Minute',
second: ' Second',
monday: 'Monday',
tuesday: 'Tuesday',
wednesday: 'Wednesday',

View File

@ -657,12 +657,14 @@ const message = {
perDay: '每天',
perNHour: '每隔 N ',
perNMinute: '每隔 N 分钟',
perNSecond: '每隔 N ',
per: '每隔',
handle: '执行',
day: '日',
day1: '天',
hour: '小时',
minute: '分钟',
second: '秒',
monday: '周一',
tuesday: '周二',
wednesday: '周三',

View File

@ -95,6 +95,7 @@
</span>
<span v-if="row.specType === 'perHour'">{{ loadZero(row.minute) }}</span>
<span v-if="row.specType === 'perNMinute'">{{ row.minute }}{{ $t('cronjob.minute') }}</span>
<span v-if="row.specType === 'perNSecond'">{{ row.second }}{{ $t('cronjob.second') }}</span>
{{ $t('cronjob.handle') }}
</template>
</el-table-column>
@ -227,6 +228,7 @@ const onOpenDialog = async (
day: 3,
hour: 1,
minute: 30,
second: 30,
keepLocal: true,
retainCopies: 7,
},

View File

@ -9,7 +9,7 @@
<el-form-item :label="$t('cronjob.taskType')" prop="type">
<el-select
v-if="dialogData.title === 'create'"
style="width: 100%"
class="selectClass"
@change="changeType"
v-model="dialogData.rowData!.type"
>
@ -27,7 +27,6 @@
<el-form-item :label="$t('cronjob.taskName')" prop="name">
<el-input
:disabled="dialogData.title === 'edit'"
style="width: 100%"
clearable
v-model.trim="dialogData.rowData!.name"
/>
@ -44,7 +43,7 @@
</el-select>
<el-select
v-if="dialogData.rowData!.specType === 'perWeek'"
style="width: 12%; margin-left: 20px"
class="specClass"
v-model="dialogData.rowData!.week"
>
<el-option
@ -54,28 +53,30 @@
:label="item.label"
/>
</el-select>
<el-input
v-if="dialogData.rowData!.specType === 'perMonth' || dialogData.rowData!.specType === 'perNDay'"
style="width: 20%; margin-left: 20px"
v-model.number="dialogData.rowData!.day"
>
<el-input v-if="hasDay()" class="specClass" v-model.number="dialogData.rowData!.day">
<template #append>{{ $t('cronjob.day') }}</template>
</el-input>
<el-input
v-if="dialogData.rowData!.specType !== 'perHour' && dialogData.rowData!.specType !== 'perNMinute'"
style="width: 20%; margin-left: 20px"
v-model.number="dialogData.rowData!.hour"
>
<el-input v-if="hasHour()" class="specClass" v-model.number="dialogData.rowData!.hour">
<template #append>{{ $t('cronjob.hour') }}</template>
</el-input>
<el-input style="width: 20%; margin-left: 20px" v-model.number="dialogData.rowData!.minute">
<el-input
v-if="dialogData.rowData!.specType !== 'perNSecond'"
class="specClass"
v-model.number="dialogData.rowData!.minute"
>
<template #append>{{ $t('cronjob.minute') }}</template>
</el-input>
<el-input
v-if="dialogData.rowData!.specType === 'perNSecond'"
class="specClass"
v-model.number="dialogData.rowData!.second"
>
<template #append>{{ $t('cronjob.second') }}</template>
</el-input>
</el-form-item>
<el-form-item v-if="hasScript()" :label="$t('cronjob.shellContent')" prop="script">
<el-input
style="width: 100%"
clearable
type="textarea"
:autosize="{ minRows: 3, maxRows: 6 }"
@ -88,7 +89,7 @@
:label="dialogData.rowData!.type === 'website' ? $t('cronjob.website'):$t('website.website')"
prop="website"
>
<el-select style="width: 100%" v-model="dialogData.rowData!.website">
<el-select class="selectClass" v-model="dialogData.rowData!.website">
<el-option :label="$t('commons.table.all')" value="all" />
<el-option v-for="item in websiteOptions" :key="item" :value="item" :label="item" />
</el-select>
@ -99,7 +100,7 @@
<div v-if="dialogData.rowData!.type === 'database'">
<el-form-item :label="$t('cronjob.database')" prop="dbName">
<el-select style="width: 100%" clearable v-model="dialogData.rowData!.dbName">
<el-select class="selectClass" clearable v-model="dialogData.rowData!.dbName">
<el-option :label="$t('commons.table.all')" value="all" />
<el-option v-for="item in mysqlInfo.dbNames" :key="item" :label="item" :value="item" />
</el-select>
@ -111,7 +112,7 @@
:label="$t('cronjob.sourceDir')"
prop="sourceDir"
>
<el-input style="width: 100%" v-model="dialogData.rowData!.sourceDir">
<el-input v-model="dialogData.rowData!.sourceDir">
<template #prepend>
<FileList @choose="loadDir" :dir="true"></FileList>
</template>
@ -120,7 +121,7 @@
<div v-if="isBackup()">
<el-form-item :label="$t('cronjob.target')" prop="targetDirID">
<el-select style="width: 100%" v-model="dialogData.rowData!.targetDirID">
<el-select class="selectClass" v-model="dialogData.rowData!.targetDirID">
<el-option
v-for="item in backupOptions"
:key="item.label"
@ -148,7 +149,7 @@
</el-form-item>
<el-form-item v-if="dialogData.rowData!.type === 'curl'" :label="$t('cronjob.url')" prop="url">
<el-input style="width: 100%" clearable v-model.trim="dialogData.rowData!.url" />
<el-input clearable v-model.trim="dialogData.rowData!.url" />
</el-form-item>
<el-form-item
@ -157,7 +158,6 @@
prop="exclusionRules"
>
<el-input
style="width: 100%"
type="textarea"
:placeholder="$t('cronjob.rulesHelper')"
:autosize="{ minRows: 3, maxRows: 6 }"
@ -284,6 +284,11 @@ const varifySpec = (rule: any, value: any, callback: any) => {
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
}
break;
case 'perNSecond':
if (!Number.isInteger(dialogData.value.rowData!.second)) {
callback(new Error(i18n.global.t('cronjob.cronSpecRule')));
}
break;
}
callback();
};
@ -296,6 +301,7 @@ const specOptions = [
{ label: i18n.global.t('cronjob.perNDay'), value: 'perNDay' },
{ label: i18n.global.t('cronjob.perNHour'), value: 'perNHour' },
{ label: i18n.global.t('cronjob.perNMinute'), value: 'perNMinute' },
{ label: i18n.global.t('cronjob.perNSecond'), value: 'perNSecond' },
];
const weekOptions = [
{ label: i18n.global.t('cronjob.monday'), value: 1 },
@ -335,6 +341,17 @@ const loadDir = async (path: string) => {
dialogData.value.rowData!.sourceDir = path;
};
const hasDay = () => {
return dialogData.value.rowData!.specType === 'perMonth' || dialogData.value.rowData!.specType === 'perNDay';
};
const hasHour = () => {
return (
dialogData.value.rowData!.specType !== 'perHour' &&
dialogData.value.rowData!.specType !== 'perNMinute' &&
dialogData.value.rowData!.specType !== 'perNSecond'
);
};
const changeType = () => {
switch (dialogData.value.rowData!.type) {
case 'shell':
@ -430,6 +447,8 @@ function checkScript() {
return row.hour > 0 && row.hour < 8784 && row.minute >= 0 && row.minute < 60;
case 'perNMinute':
return row.minute > 0 && row.minute < 527040;
case 'perNSecond':
return row.second > 0 && row.second < 31622400;
}
}
@ -438,6 +457,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
dialogData.value.rowData.day = Number(dialogData.value.rowData.day);
dialogData.value.rowData.hour = Number(dialogData.value.rowData.hour);
dialogData.value.rowData.minute = Number(dialogData.value.rowData.minute);
dialogData.value.rowData.second = Number(dialogData.value.rowData.second);
if (!checkScript()) {
MsgError(i18n.global.t('cronjob.cronSpecHelper'));
return;
@ -463,3 +483,12 @@ defineExpose({
acceptParams,
});
</script>
<style scoped lang="scss">
.specClass {
width: 20%;
margin-left: 20px;
}
.selectClass {
width: 100%;
}
</style>