mirror of
https://github.com/whyour/qinglong.git
synced 2025-07-27 14:46:06 +08:00
定时任务支持批量启用禁用删除运行
This commit is contained in:
parent
2092e93dc2
commit
5d43add6eb
|
@ -51,18 +51,16 @@ export default (app: Router) => {
|
|||
},
|
||||
);
|
||||
|
||||
route.get(
|
||||
'/crons/:id/run',
|
||||
route.put(
|
||||
'/crons/run',
|
||||
celebrate({
|
||||
params: Joi.object({
|
||||
id: Joi.string().required(),
|
||||
}),
|
||||
body: Joi.array().items(Joi.string().required()),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const cronService = Container.get(CronService);
|
||||
const data = await cronService.run(req.params.id);
|
||||
const data = await cronService.run(req.body);
|
||||
return res.send({ code: 200, data });
|
||||
} catch (e) {
|
||||
logger.error('🔥 error: %o', e);
|
||||
|
@ -71,18 +69,16 @@ export default (app: Router) => {
|
|||
},
|
||||
);
|
||||
|
||||
route.get(
|
||||
'/crons/:id/disable',
|
||||
route.put(
|
||||
'/crons/disable',
|
||||
celebrate({
|
||||
params: Joi.object({
|
||||
id: Joi.string().required(),
|
||||
}),
|
||||
body: Joi.array().items(Joi.string().required()),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const cronService = Container.get(CronService);
|
||||
const data = await cronService.disabled(req.params.id);
|
||||
const data = await cronService.disabled(req.body);
|
||||
return res.send({ code: 200, data });
|
||||
} catch (e) {
|
||||
logger.error('🔥 error: %o', e);
|
||||
|
@ -91,18 +87,16 @@ export default (app: Router) => {
|
|||
},
|
||||
);
|
||||
|
||||
route.get(
|
||||
'/crons/:id/enable',
|
||||
route.put(
|
||||
'/crons/enable',
|
||||
celebrate({
|
||||
params: Joi.object({
|
||||
id: Joi.string().required(),
|
||||
}),
|
||||
body: Joi.array().items(Joi.string().required()),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const cronService = Container.get(CronService);
|
||||
const data = await cronService.enabled(req.params.id);
|
||||
const data = await cronService.enabled(req.body);
|
||||
return res.send({ code: 200, data });
|
||||
} catch (e) {
|
||||
logger.error('🔥 error: %o', e);
|
||||
|
@ -159,17 +153,15 @@ export default (app: Router) => {
|
|||
);
|
||||
|
||||
route.delete(
|
||||
'/crons/:id',
|
||||
'/crons',
|
||||
celebrate({
|
||||
params: Joi.object({
|
||||
id: Joi.string().required(),
|
||||
}),
|
||||
body: Joi.array().items(Joi.string().required()),
|
||||
}),
|
||||
async (req: Request, res: Response, next: NextFunction) => {
|
||||
const logger: Logger = Container.get('logger');
|
||||
try {
|
||||
const cronService = Container.get(CronService);
|
||||
const data = await cronService.remove(req.params.id);
|
||||
const data = await cronService.remove(req.body);
|
||||
return res.send({ code: 200, data });
|
||||
} catch (e) {
|
||||
logger.error('🔥 error: %o', e);
|
||||
|
|
|
@ -33,7 +33,7 @@ const run = async () => {
|
|||
) {
|
||||
schedule.scheduleJob(task.schedule, function () {
|
||||
let command = task.command as string;
|
||||
if (!command.startsWith('task ') && !command.startsWith('ql ')) {
|
||||
if (!command.includes('task ') && !command.includes('ql ')) {
|
||||
command = `task ${command}`;
|
||||
}
|
||||
exec(command);
|
||||
|
|
|
@ -74,8 +74,8 @@ export default class CronService {
|
|||
this.cronDb.update({ _id }, { $set: { stopped, saved: false } });
|
||||
}
|
||||
|
||||
public async remove(_id: string) {
|
||||
this.cronDb.remove({ _id }, {});
|
||||
public async remove(ids: string[]) {
|
||||
this.cronDb.remove({ _id: { $in: ids } }, { multi: true });
|
||||
await this.set_crontab();
|
||||
}
|
||||
|
||||
|
@ -112,19 +112,27 @@ export default class CronService {
|
|||
});
|
||||
}
|
||||
|
||||
public async run(_id: string) {
|
||||
this.cronDb.find({ _id }).exec((err, docs: Crontab[]) => {
|
||||
let res = docs[0];
|
||||
public async run(ids: string[]) {
|
||||
this.cronDb.find({ _id: { $in: ids } }).exec((err, docs: Crontab[]) => {
|
||||
for (let i = 0; i < docs.length; i++) {
|
||||
const doc = docs[i];
|
||||
this.runSingle(doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async runSingle(cron: Crontab) {
|
||||
let { _id, command } = cron;
|
||||
|
||||
this.logger.silly('Running job');
|
||||
this.logger.silly('ID: ' + _id);
|
||||
this.logger.silly('Original command: ' + res.command);
|
||||
this.logger.silly('Original command: ' + command);
|
||||
|
||||
let logFile = `${config.manualLogPath}${res._id}.log`;
|
||||
let logFile = `${config.manualLogPath}${_id}.log`;
|
||||
fs.writeFileSync(logFile, `开始执行...\n\n${new Date().toString()}\n`);
|
||||
|
||||
let cmdStr = res.command;
|
||||
if (!cmdStr.startsWith('task') && !cmdStr.startsWith('ql ')) {
|
||||
let cmdStr = command;
|
||||
if (!cmdStr.includes('task ') && !cmdStr.includes('ql ')) {
|
||||
cmdStr = `task ${cmdStr}`;
|
||||
}
|
||||
if (cmdStr.endsWith('.js')) {
|
||||
|
@ -165,16 +173,23 @@ export default class CronService {
|
|||
this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } });
|
||||
fs.appendFileSync(logFile, `\n\n连接断开...`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async disabled(_id: string) {
|
||||
this.cronDb.update({ _id }, { $set: { status: CrontabStatus.disabled } });
|
||||
public async disabled(ids: string[]) {
|
||||
this.cronDb.update(
|
||||
{ _id: { $in: ids } },
|
||||
{ $set: { status: CrontabStatus.disabled } },
|
||||
{ multi: true },
|
||||
);
|
||||
await this.set_crontab();
|
||||
}
|
||||
|
||||
public async enabled(_id: string) {
|
||||
this.cronDb.update({ _id }, { $set: { status: CrontabStatus.idle } });
|
||||
public async enabled(ids: string[]) {
|
||||
this.cronDb.update(
|
||||
{ _id: { $in: ids } },
|
||||
{ $set: { status: CrontabStatus.idle } },
|
||||
{ multi: true },
|
||||
);
|
||||
}
|
||||
|
||||
public async log(_id: string) {
|
||||
|
|
|
@ -434,7 +434,6 @@ const Config = () => {
|
|||
<PageContainer
|
||||
className="cookie-wrapper"
|
||||
title="Cookie管理"
|
||||
loading={loading}
|
||||
extra={[
|
||||
<Button key="2" type="primary" onClick={() => addCookie()}>
|
||||
添加Cookie
|
||||
|
@ -463,9 +462,9 @@ const Config = () => {
|
|||
dataSource={value}
|
||||
rowKey="value"
|
||||
size="middle"
|
||||
bordered
|
||||
scroll={{ x: 768 }}
|
||||
components={components}
|
||||
loading={loading}
|
||||
onRow={(record, index) => {
|
||||
return {
|
||||
index,
|
||||
|
|
|
@ -39,6 +39,18 @@ enum CrontabStatus {
|
|||
'disabled',
|
||||
}
|
||||
|
||||
enum OperationName {
|
||||
'启用',
|
||||
'禁用',
|
||||
'运行',
|
||||
}
|
||||
|
||||
enum OperationPath {
|
||||
'enable',
|
||||
'disable',
|
||||
'run',
|
||||
}
|
||||
|
||||
const Crontab = () => {
|
||||
const columns = [
|
||||
{
|
||||
|
@ -142,6 +154,7 @@ const Crontab = () => {
|
|||
const [searchText, setSearchText] = useState('');
|
||||
const [isLogModalVisible, setIsLogModalVisible] = useState(false);
|
||||
const [logCron, setLogCron] = useState<any>();
|
||||
const [selectedRowIds, setSelectedRowIds] = useState<string[]>([]);
|
||||
|
||||
const getCrons = () => {
|
||||
setLoading(true);
|
||||
|
@ -177,7 +190,7 @@ const Crontab = () => {
|
|||
),
|
||||
onOk() {
|
||||
request
|
||||
.delete(`${config.apiPrefix}crons/${record._id}`)
|
||||
.delete(`${config.apiPrefix}crons`, { data: [record._id] })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
notification.success({
|
||||
|
@ -213,7 +226,7 @@ const Crontab = () => {
|
|||
),
|
||||
onOk() {
|
||||
request
|
||||
.get(`${config.apiPrefix}crons/${record._id}/run`)
|
||||
.put(`${config.apiPrefix}crons/run`, { data: [record._id] })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
const result = [...value];
|
||||
|
@ -252,21 +265,16 @@ const Crontab = () => {
|
|||
),
|
||||
onOk() {
|
||||
request
|
||||
.get(
|
||||
`${config.apiPrefix}crons/${record._id}/${
|
||||
.put(
|
||||
`${config.apiPrefix}crons/${
|
||||
record.status === CrontabStatus.disabled ? 'enable' : 'disable'
|
||||
}`,
|
||||
{
|
||||
data: { _id: record._id },
|
||||
data: [record._id],
|
||||
},
|
||||
)
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
notification.success({
|
||||
message: `${
|
||||
record.status === CrontabStatus.disabled ? '启用' : '禁用'
|
||||
}成功`,
|
||||
});
|
||||
const newStatus =
|
||||
record.status === CrontabStatus.disabled
|
||||
? CrontabStatus.idle
|
||||
|
@ -296,7 +304,7 @@ const Crontab = () => {
|
|||
}> = ({ record, index }) => (
|
||||
<Dropdown
|
||||
arrow
|
||||
trigger={['click', 'hover']}
|
||||
trigger={['click']}
|
||||
overlay={
|
||||
<Menu onClick={({ key }) => action(key, record, index)}>
|
||||
<Menu.Item key="edit" icon={<EditOutlined />}>
|
||||
|
@ -383,6 +391,74 @@ const Crontab = () => {
|
|||
.finally(() => setLoading(false));
|
||||
};
|
||||
|
||||
const onSelectChange = (selectedIds: any[]) => {
|
||||
setSelectedRowIds(selectedIds);
|
||||
};
|
||||
|
||||
const rowSelection = {
|
||||
selectedRowIds,
|
||||
onChange: onSelectChange,
|
||||
selections: [
|
||||
Table.SELECTION_ALL,
|
||||
Table.SELECTION_INVERT,
|
||||
Table.SELECTION_NONE,
|
||||
],
|
||||
};
|
||||
|
||||
const delCrons = () => {
|
||||
Modal.confirm({
|
||||
title: '确认删除',
|
||||
content: <>确认删除选中的定时任务吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.delete(`${config.apiPrefix}crons`, { data: selectedRowIds })
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
notification.success({
|
||||
message: '批量删除成功',
|
||||
});
|
||||
getCrons();
|
||||
} else {
|
||||
notification.error({
|
||||
message: data,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const operateCrons = (operationStatus: number) => {
|
||||
Modal.confirm({
|
||||
title: `确认${OperationName[operationStatus]}`,
|
||||
content: <>确认{OperationName[operationStatus]}选中的定时任务吗</>,
|
||||
onOk() {
|
||||
request
|
||||
.put(`${config.apiPrefix}crons/${OperationPath[operationStatus]}`, {
|
||||
data: selectedRowIds,
|
||||
})
|
||||
.then((data: any) => {
|
||||
if (data.code === 200) {
|
||||
notification.success({
|
||||
message: `批量${OperationName[operationStatus]}成功`,
|
||||
});
|
||||
getCrons();
|
||||
} else {
|
||||
notification.error({
|
||||
message: data,
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
console.log('Cancel');
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (logCron) {
|
||||
localStorage.setItem('logCron', logCron._id);
|
||||
|
@ -410,7 +486,6 @@ const Crontab = () => {
|
|||
<PageContainer
|
||||
className="code-mirror-wrapper"
|
||||
title="定时任务"
|
||||
loading={loading}
|
||||
extra={[
|
||||
<Search
|
||||
placeholder="请输入名称或者关键词"
|
||||
|
@ -439,6 +514,38 @@ const Crontab = () => {
|
|||
height: '100vh',
|
||||
}}
|
||||
>
|
||||
{selectedRowIds.length > 0 && (
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Button type="primary" onClick={delCrons}>
|
||||
批量删除
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => operateCrons(0)}
|
||||
style={{ marginLeft: 8 }}
|
||||
>
|
||||
批量启用
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => operateCrons(1)}
|
||||
style={{ marginLeft: 8 }}
|
||||
>
|
||||
批量禁用
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => operateCrons(2)}
|
||||
style={{ marginLeft: 8 }}
|
||||
>
|
||||
批量运行
|
||||
</Button>
|
||||
<span style={{ marginLeft: 8 }}>
|
||||
已选择
|
||||
<a>{selectedRowIds?.length}</a>项
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<Table
|
||||
columns={columns}
|
||||
pagination={{
|
||||
|
@ -449,8 +556,9 @@ const Crontab = () => {
|
|||
dataSource={value}
|
||||
rowKey="_id"
|
||||
size="middle"
|
||||
bordered
|
||||
scroll={{ x: 768 }}
|
||||
loading={loading}
|
||||
rowSelection={rowSelection}
|
||||
/>
|
||||
<CronLogModal
|
||||
visible={isLogModalVisible}
|
||||
|
|
Loading…
Reference in New Issue
Block a user