Browse Source

feat(tasks): 增加多租户支持

pull/463/head
cKey 4 years ago
parent
commit
93a6e0d057
  1. 2
      apps/vue/src/api/auditing/model/auditLogModel.ts
  2. 67
      apps/vue/src/api/task-management/backgroundJobInfo.ts
  3. 50
      apps/vue/src/components/Permission/src/PermissionModal.vue
  4. 8
      apps/vue/src/components/Permission/src/hooks/usePermissions.ts
  5. 1
      apps/vue/src/components/Permission/src/types/permission.ts
  6. 3
      apps/vue/src/components/Permission/src/utils/helper.ts
  7. 1
      apps/vue/src/store/modules/abp.ts
  8. 4
      apps/vue/src/views/auditing/hooks/useAuditLog.ts
  9. 56
      apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoTable.vue
  10. 6
      apps/vue/src/views/task-management/background-jobs/datas/TableData.ts
  11. 39
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs
  12. 19
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN.Abp.BackgroundTasks.Hangfire.csproj
  13. 18
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/AbpBackgroundTasksHangfireModule.cs
  14. 96
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/HangfireJobExecutedAttribute.cs
  15. 122
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/HangfireJobScheduler.cs
  16. 32
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/HangfireJobSimpleAdapter.cs
  17. 36
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs
  18. 14
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs
  19. 5
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs
  20. 6
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs
  21. 6
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs
  22. 9
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoBatchInput.cs
  23. 14
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs
  24. 37
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissionDefinitionProvider.cs
  25. 1
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs
  26. 92
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs
  27. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json
  28. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json
  29. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs
  30. 8
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs
  31. 47
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs
  32. 23
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs
  33. 2
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs
  34. 55
      aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobInfoController.cs
  35. 201
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220112093435_Add-Support-Multi-Tenancy-With-Background-Jobs.Designer.cs
  36. 38
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220112093435_Add-Support-Multi-Tenancy-With-Background-Jobs.cs
  37. 8
      aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/TaskManagementMigrationsDbContextModelSnapshot.cs

2
apps/vue/src/api/auditing/model/auditLogModel.ts

@ -17,7 +17,7 @@ export interface PropertyChange {
export interface EntityChange { export interface EntityChange {
id: string; id: string;
changeTime?: Date; changeTime?: Date;
changeType?: ChangeType; changeType: ChangeType;
entityTenantId?: string; entityTenantId?: string;
entityId?: string; entityId?: string;
entityTypeFullName?: string; entityTypeFullName?: string;

67
apps/vue/src/api/task-management/backgroundJobInfo.ts

@ -17,7 +17,14 @@ enum Api {
Pause = '/api/task-management/background-jobs/{id}/pause', Pause = '/api/task-management/background-jobs/{id}/pause',
Resume = '/api/task-management/background-jobs/{id}/resume', Resume = '/api/task-management/background-jobs/{id}/resume',
Trigger = '/api/task-management/background-jobs/{id}/trigger', Trigger = '/api/task-management/background-jobs/{id}/trigger',
Start = '/api/task-management/background-jobs/{id}/start',
Stop = '/api/task-management/background-jobs/{id}/stop', Stop = '/api/task-management/background-jobs/{id}/stop',
BulkPause = '/api/task-management/background-jobs/bulk-pause',
BulkResume = '/api/task-management/background-jobs/bulk-resume',
BulkTrigger = '/api/task-management/background-jobs/bulk-trigger',
BulkStart = '/api/task-management/background-jobs/bulk-start',
BulkStop = '/api/task-management/background-jobs/bulk-stop',
BulkDelete = '/api/task-management/background-jobs/bulk-delete',
} }
export const getById = (id: string) => { export const getById = (id: string) => {
@ -71,8 +78,68 @@ export const trigger = (id: string) => {
}); });
}; };
export const start = (id: string) => {
return defAbpHttp.put<void>({
url: format(Api.Stop, { id: id }),
});
};
export const stop = (id: string) => { export const stop = (id: string) => {
return defAbpHttp.put<void>({ return defAbpHttp.put<void>({
url: format(Api.Stop, { id: id }), url: format(Api.Stop, { id: id }),
}); });
}; };
export const bulkPause = (ids: string[]) => {
return defAbpHttp.put<void>({
url: Api.BulkPause,
data: {
jobIds: ids,
},
});
};
export const bulkResume = (ids: string[]) => {
return defAbpHttp.put<void>({
url: Api.BulkResume,
data: {
jobIds: ids,
},
});
};
export const bulkTrigger = (ids: string[]) => {
return defAbpHttp.put<void>({
url: Api.BulkTrigger,
data: {
jobIds: ids,
},
});
};
export const bulkStart = (ids: string[]) => {
return defAbpHttp.put<void>({
url: Api.BulkStart,
data: {
jobIds: ids,
},
});
};
export const bulkStop = (ids: string[]) => {
return defAbpHttp.put<void>({
url: Api.BulkStop,
data: {
jobIds: ids,
},
});
};
export const bulkDelete = (ids: string[]) => {
return defAbpHttp.put<void>({
url: Api.BulkDelete,
data: {
jobIds: ids,
},
});
};

50
apps/vue/src/components/Permission/src/PermissionModal.vue

@ -6,6 +6,7 @@
:width="800" :width="800"
:min-height="600" :min-height="600"
@ok="handleSubmit" @ok="handleSubmit"
@visible-change="handleVisibleChange"
> >
<Row> <Row>
<Col :span="24"> <Col :span="24">
@ -35,7 +36,6 @@
<BasicTree <BasicTree
:checkable="true" :checkable="true"
:checkStrictly="true" :checkStrictly="true"
:selectable="false"
:disabled="permissionTreeDisabled" :disabled="permissionTreeDisabled"
:treeData="permission.children" :treeData="permission.children"
:replaceFields="{ :replaceFields="{
@ -104,10 +104,28 @@
} = usePermissions({ } = usePermissions({
getPropsRef: model, getPropsRef: model,
}); });
const [registerModal, { closeModal, setModalProps }] = useModalInner((val) => { const [registerModal, { closeModal, changeOkLoading }] = useModalInner((val) => {
model.value = val; model.value = val;
}); });
function handleVisibleChange(visible: boolean) {
if (!visible) {
model.value.providerKey = '';
}
}
function handleSubmit() {
changeOkLoading(true);
handleSavePermission()
.then(() => {
message.success(L('Successful'));
closeModal();
})
.finally(() => {
changeOkLoading(false);
});
}
return { return {
L, L,
activeKey, activeKey,
@ -119,36 +137,12 @@
permissionTreeCheckState, permissionTreeCheckState,
permissionTreeDisabled, permissionTreeDisabled,
handlePermissionGranted, handlePermissionGranted,
handleSavePermission,
handleGrantAllPermission, handleGrantAllPermission,
handleGrantPermissions, handleGrantPermissions,
registerModal, registerModal,
closeModal, handleSubmit,
setModalProps, handleVisibleChange,
}; };
}, },
methods: {
handleSubmit() {
this.setModalProps({
loading: true,
confirmLoading: true,
showCancelBtn: false,
closable: false,
});
this.handleSavePermission()
.then(() => {
message.success(this.L('Successful'));
this.closeModal();
})
.finally(() => {
this.setModalProps({
loading: false,
confirmLoading: false,
showCancelBtn: true,
closable: true,
});
});
},
},
}); });
</script> </script>

8
apps/vue/src/components/Permission/src/hooks/usePermissions.ts

@ -2,7 +2,7 @@ import type { Ref } from 'vue';
import type { PermissionProps, PermissionTree } from '../types/permission'; import type { PermissionProps, PermissionTree } from '../types/permission';
import { computed, watch, unref, ref } from 'vue'; import { computed, watch, unref, ref } from 'vue';
import { useI18n } from '/@/hooks/web/useI18n'; import { useLocalization } from '/@/hooks/abp/useLocalization';
import { get, update } from '/@/api/permission-management/permission'; import { get, update } from '/@/api/permission-management/permission';
import { PermissionProvider } from '/@/api/permission-management/model/permissionModel'; import { PermissionProvider } from '/@/api/permission-management/model/permissionModel';
import { import {
@ -24,7 +24,7 @@ interface UsePermission {
} }
export function usePermissions({ getPropsRef }: UsePermission) { export function usePermissions({ getPropsRef }: UsePermission) {
const { t } = useI18n(); const { L } = useLocalization('AbpPermissionManagement');
/** 弹出层标题 */ /** 弹出层标题 */
const title = ref(''); const title = ref('');
/** 权限树 */ /** 权限树 */
@ -36,7 +36,7 @@ export function usePermissions({ getPropsRef }: UsePermission) {
*/ */
function handleGetPermission(name: string, key?: string) { function handleGetPermission(name: string, key?: string) {
get({ providerName: name, providerKey: key }).then((res) => { get({ providerName: name, providerKey: key }).then((res) => {
title.value = `${t('AbpPermissionManagement.Permissions')} - ${res.entityDisplayName}`; title.value = `${L('Permissions')} - ${res.entityDisplayName}`;
permissionTree.value = generatePermissionTree(res.groups); permissionTree.value = generatePermissionTree(res.groups);
}); });
} }
@ -145,8 +145,10 @@ export function usePermissions({ getPropsRef }: UsePermission) {
() => unref(getPropsRef).providerKey, () => unref(getPropsRef).providerKey,
(key) => { (key) => {
permissionTree.value = []; permissionTree.value = [];
if (key) {
const props = unref(getPropsRef); const props = unref(getPropsRef);
handleGetPermission(props.providerName, key); handleGetPermission(props.providerName, key);
}
}, },
); );

1
apps/vue/src/components/Permission/src/types/permission.ts

@ -5,6 +5,7 @@ export interface PermissionProps {
} }
export interface PermissionTree { export interface PermissionTree {
isRoot: boolean;
/** 权限标识 */ /** 权限标识 */
name: string; name: string;
/** 显示名称 */ /** 显示名称 */

3
apps/vue/src/components/Permission/src/utils/helper.ts

@ -7,6 +7,7 @@ export function generatePermissionTree(permissionGroups: PermissionGroup[]) {
const trees: PermissionTree[] = []; const trees: PermissionTree[] = [];
permissionGroups.forEach((g) => { permissionGroups.forEach((g) => {
const tree: PermissionTree = { const tree: PermissionTree = {
isRoot: true,
name: g.name, name: g.name,
displayName: g.displayName, displayName: g.displayName,
disabled: false, disabled: false,
@ -93,7 +94,7 @@ export function getParentList(children: PermissionTree[], name: string) {
export function toPermissionList(treeList: PermissionTree[]) { export function toPermissionList(treeList: PermissionTree[]) {
const permissions: IPermission[] = []; const permissions: IPermission[] = [];
for (let index = 0; index < treeList.length; index++) { for (let index = 0; index < treeList.length; index++) {
if (treeList[index].isGranted !== undefined) { if (!treeList[index].isRoot && treeList[index].isGranted !== undefined) {
permissions.push({ permissions.push({
name: treeList[index].name, name: treeList[index].name,
isGranted: treeList[index].isGranted === true, isGranted: treeList[index].isGranted === true,

1
apps/vue/src/store/modules/abp.ts

@ -12,7 +12,6 @@ import {
Auth, Auth,
CurrentUser, CurrentUser,
Localization, Localization,
Setting,
} from '/@/api/abp/model/appModel'; } from '/@/api/abp/model/appModel';
import { ApplicationApiDescriptionModel } from '/@/api/abp/model/apiDefinition'; import { ApplicationApiDescriptionModel } from '/@/api/abp/model/apiDefinition';

4
apps/vue/src/views/auditing/hooks/useAuditLog.ts

@ -18,10 +18,10 @@ export function useAuditLog() {
['PATCH']: 'pink', ['PATCH']: 'pink',
}; };
const entityChangeTypeColor = computed(() => { const entityChangeTypeColor = computed(() => {
return (changeType?: ChangeType) => (changeType ? changeTypeColorMap[changeType].color : ''); return (changeType: ChangeType) => changeTypeColorMap[changeType].color;
}); });
const entityChangeType = computed(() => { const entityChangeType = computed(() => {
return (changeType?: ChangeType) => (changeType ? changeTypeColorMap[changeType].value : ''); return (changeType: ChangeType) => changeTypeColorMap[changeType].value;
}); });
const httpMethodColor = computed(() => { const httpMethodColor = computed(() => {
return (method?: string) => { return (method?: string) => {

56
apps/vue/src/views/task-management/background-jobs/components/BackgroundJobInfoTable.vue

@ -2,6 +2,20 @@
<div class="content"> <div class="content">
<BasicTable @register="registerTable"> <BasicTable @register="registerTable">
<template #toolbar> <template #toolbar>
<a-button
v-if="hasPermission('TaskManagement.BackgroundJobs.Start')"
:disabled="!isMultiSelected"
@click="handleStart"
>{{ L('BackgroundJobs:Start') }}</a-button
>
<a-button
v-if="hasPermission('TaskManagement.BackgroundJobs.Stop')"
type="primary"
danger
:disabled="!isMultiSelected"
@click="handleStop"
>{{ L('BackgroundJobs:Stop') }}</a-button
>
<a-button <a-button
v-if="hasPermission('TaskManagement.BackgroundJobs.Create')" v-if="hasPermission('TaskManagement.BackgroundJobs.Create')"
type="primary" type="primary"
@ -61,12 +75,6 @@
ifShow: [JobStatus.Running, JobStatus.Completed, JobStatus.FailedRetry].includes(record.status), ifShow: [JobStatus.Running, JobStatus.Completed, JobStatus.FailedRetry].includes(record.status),
onClick: handleTrigger.bind(null, record), onClick: handleTrigger.bind(null, record),
}, },
{
auth: 'TaskManagement.BackgroundJobs.Stop',
label: L('BackgroundJobs:Stop'),
ifShow: [JobStatus.Running, JobStatus.FailedRetry].includes(record.status),
onClick: handleStop.bind(null, record),
},
]" ]"
/> />
</template> </template>
@ -76,13 +84,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue';
import { Switch, Modal, Tag, Tooltip, message } from 'ant-design-vue'; import { Switch, Modal, Tag, Tooltip, message } from 'ant-design-vue';
import { useLocalization } from '/@/hooks/abp/useLocalization'; import { useLocalization } from '/@/hooks/abp/useLocalization';
import { usePermission } from '/@/hooks/web/usePermission'; import { usePermission } from '/@/hooks/web/usePermission';
import { useModal } from '/@/components/Modal'; import { useModal } from '/@/components/Modal';
import { BasicTable, TableAction, useTable } from '/@/components/Table'; import { BasicTable, TableAction, useTable } from '/@/components/Table';
import { formatPagedRequest } from '/@/utils/http/abp/helper'; import { formatPagedRequest } from '/@/utils/http/abp/helper';
import { getList, deleteById, pause, resume, trigger, stop } from '/@/api/task-management/backgroundJobInfo'; import { getList, pause, resume, trigger, deleteById, bulkStop, bulkStart } from '/@/api/task-management/backgroundJobInfo';
import { JobStatus } from '/@/api/task-management/model/backgroundJobInfoModel'; import { JobStatus } from '/@/api/task-management/model/backgroundJobInfoModel';
import { getDataColumns } from '../datas/TableData'; import { getDataColumns } from '../datas/TableData';
import { getSearchFormSchemas } from '../datas/ModalData'; import { getSearchFormSchemas } from '../datas/ModalData';
@ -92,7 +101,7 @@
const { L } = useLocalization('TaskManagement'); const { L } = useLocalization('TaskManagement');
const { hasPermission } = usePermission(); const { hasPermission } = usePermission();
const [registerModal, { openModal }] = useModal(); const [registerModal, { openModal }] = useModal();
const [registerTable, { reload }] = useTable({ const [registerTable, { reload, getSelectRowKeys }] = useTable({
rowKey: 'id', rowKey: 'id',
title: L('BackgroundJobs'), title: L('BackgroundJobs'),
columns: getDataColumns(), columns: getDataColumns(),
@ -106,8 +115,12 @@
showIndexColumn: false, showIndexColumn: false,
canResize: false, canResize: false,
immediate: true, immediate: true,
rowSelection: { type: 'radio' }, clickToRowSelect: false,
formConfig: getSearchFormSchemas(), formConfig: getSearchFormSchemas(),
rowSelection: {
type: 'checkbox',
onChange: handleSelectChange,
},
actionColumn: { actionColumn: {
width: 220, width: 220,
title: L('Actions'), title: L('Actions'),
@ -115,6 +128,14 @@
slots: { customRender: 'action' }, slots: { customRender: 'action' },
}, },
}); });
const selectedRowKeys = ref<string[]>([]);
const isMultiSelected = computed(() => {
return selectedRowKeys.value.length > 0;
});
function handleSelectChange(keys) {
selectedRowKeys.value = keys;
}
function handleChange() { function handleChange() {
reload(); reload();
@ -149,8 +170,17 @@
}); });
} }
function handleStop(record) { function handleStart() {
stop(record.id).then(() => { const selectKeys = getSelectRowKeys();
bulkStart(selectKeys).then(() => {
message.success(L('Successful'));
reload();
});
}
function handleStop() {
const selectKeys = getSelectRowKeys();
bulkStop(selectKeys).then(() => {
message.success(L('Successful')); message.success(L('Successful'));
reload(); reload();
}); });
@ -159,10 +189,10 @@
function handleDelete(record) { function handleDelete(record) {
Modal.warning({ Modal.warning({
title: L('AreYouSure'), title: L('AreYouSure'),
content: L('ItemWillBeDeletedMessage'), content: L('MultipleSelectJobsWillBeDeletedMessage'),
okCancel: true, okCancel: true,
onOk: () => { onOk: () => {
deleteById(record.id).then(() => { deleteById(record).then(() => {
reload(); reload();
}); });
}, },

6
apps/vue/src/views/task-management/background-jobs/datas/TableData.ts

@ -18,6 +18,7 @@ export function getDataColumns(): BasicColumn[] {
align: 'left', align: 'left',
width: 150, width: 150,
sorter: true, sorter: true,
fixed: 'left',
}, },
{ {
title: L('DisplayName:Name'), title: L('DisplayName:Name'),
@ -25,10 +26,11 @@ export function getDataColumns(): BasicColumn[] {
align: 'left', align: 'left',
width: 300, width: 300,
sorter: true, sorter: true,
fixed: 'left',
}, },
{ {
title: L('DisplayName:Type'), title: L('DisplayName:Description'),
dataIndex: 'type', dataIndex: 'description',
align: 'left', align: 'left',
width: 350, width: 350,
sorter: true, sorter: true,

39
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Abstractions/LINGYUN/Abp/BackgroundTasks/JobInfo.cs

@ -10,6 +10,10 @@ public class JobInfo
/// </summary> /// </summary>
public Guid Id { get; set; } public Guid Id { get; set; }
/// <summary> /// <summary>
/// 租户标识
/// </summary>
public Guid? TenantId { get; set; }
/// <summary>
/// 任务名称 /// 任务名称
/// </summary> /// </summary>
public string Name { get; set; } public string Name { get; set; }
@ -101,4 +105,39 @@ public class JobInfo
/// 0或更小不生效 /// 0或更小不生效
/// </summary> /// </summary>
public int LockTimeOut { get; set; } public int LockTimeOut { get; set; }
public int GetCanBeTriggered()
{
// 无限次
var maxCount = -1;
// 剩余次数
if (MaxCount > 0)
{
maxCount = MaxCount - TriggerCount;
if (maxCount < 0)
{
maxCount = 0;
}
}
// 一次
if (JobType == JobType.Once)
{
maxCount = 1;
}
// 重试
if (Status == JobStatus.FailedRetry)
{
maxCount = MaxTryCount - TryCount;
if (maxCount < 0)
{
maxCount = 0;
}
}
return maxCount;
}
} }

19
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN.Abp.BackgroundTasks.Hangfire.csproj

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\configureawait.props" />
<Import Project="..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.Hangfire" Version="$(VoloAbpPackageVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\LINGYUN.Abp.BackgroundTasks\LINGYUN.Abp.BackgroundTasks.csproj" />
</ItemGroup>
</Project>

18
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/AbpBackgroundTasksHangfireModule.cs

@ -0,0 +1,18 @@
using Hangfire;
using Volo.Abp.Hangfire;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.BackgroundTasks.Hangfire;
[DependsOn(typeof(AbpBackgroundTasksModule))]
[DependsOn(typeof(AbpHangfireModule))]
public class AbpBackgroundTasksHangfireModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddHangfire((provider, configuration) =>
{
configuration.UseFilter(new HangfireJobExecutedAttribute(provider));
});
}
}

96
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/HangfireJobExecutedAttribute.cs

@ -0,0 +1,96 @@
using Hangfire.Common;
using Hangfire.Server;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using System;
using System.Linq;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Hangfire;
public class HangfireJobExecutedAttribute : JobFilterAttribute, IServerFilter
{
public ILogger<HangfireJobExecutedAttribute> Logger { protected get; set; }
public IServiceProvider ServiceProvider { get; set; }
public HangfireJobExecutedAttribute()
{
Logger = NullLogger<HangfireJobExecutedAttribute>.Instance;
}
public HangfireJobExecutedAttribute(IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
Logger = NullLogger<HangfireJobExecutedAttribute>.Instance;
}
public async void OnPerformed(PerformedContext filterContext)
{
if (Guid.TryParse(filterContext.BackgroundJob.Id, out var jobUUId))
{
try
{
var jobEventProvider = ServiceProvider.GetRequiredService<IJobEventProvider>();
var jobEventList = jobEventProvider.GetAll();
if (!jobEventList.Any())
{
return;
}
using var scope = ServiceProvider.CreateScope();
var jobGroup = filterContext.Connection
.GetJobParameter(filterContext.BackgroundJob.Id, nameof(JobInfo.Group));
var jobName = filterContext.Connection
.GetJobParameter(filterContext.BackgroundJob.Id, nameof(JobInfo.Name));
var jobEventData = new JobEventData(
jobUUId,
filterContext.BackgroundJob.Job.Type,
jobGroup,
jobName)
{
Result = filterContext.Result?.ToString()
};
var eventContext = new JobEventContext(
scope.ServiceProvider,
jobEventData);
var index = 0;
var taskList = new Task[jobEventList.Count];
foreach (var jobEvent in jobEventList)
{
taskList[index] = jobEvent.OnJobBeforeExecuted(eventContext);
index++;
}
await Task.WhenAll(taskList);
}
catch (Exception ex)
{
Logger.LogError($"The event before the task execution is abnormal:{ex}");
}
}
}
public async void OnPerforming(PerformingContext filterContext)
{
var lockTime = filterContext.Connection
.GetJobParameter(filterContext.BackgroundJob.Id, nameof(JobInfo.LockTimeOut));
if (!lockTime.IsNullOrWhiteSpace() && int.TryParse(lockTime, out var time) && time > 0)
{
var jobLockProvider = ServiceProvider.GetRequiredService<IJobLockProvider>();
if (!await jobLockProvider.TryLockAsync(
filterContext.BackgroundJob.Id,
time,
filterContext.CancellationToken.ShutdownToken))
{
filterContext.Canceled = true;
}
}
}
}

122
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/HangfireJobScheduler.cs

@ -0,0 +1,122 @@
using Hangfire;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace LINGYUN.Abp.BackgroundTasks.Hangfire;
[Dependency(ReplaceServices = true)]
public class HangfireJobScheduler : IJobScheduler, ISingletonDependency
{
public ILogger<HangfireJobScheduler> Logger { protected get; set; }
protected AbpBackgroundTasksOptions Options { get; }
protected JobStorage JobStorage { get; }
protected IRecurringJobManager RecurringJobManager { get; }
public HangfireJobScheduler(
JobStorage jobStorage,
IOptions<AbpBackgroundTasksOptions> options)
{
Options = options.Value;
JobStorage = jobStorage;
RecurringJobManager = new RecurringJobManager(jobStorage);
Logger = NullLogger<HangfireJobScheduler>.Instance;
}
public Task<bool> ExistsAsync(JobInfo job)
{
var monitor = JobStorage.GetMonitoringApi();
monitor.JobDetails(job.);
}
public Task PauseAsync(JobInfo job)
{
throw new NotImplementedException();
}
public virtual Task<bool> QueueAsync(JobInfo job)
{
var jobType = Options.JobProviders.GetOrDefault(job.Type) ?? Type.GetType(job.Type, false);
if (jobType == null)
{
Logger.LogWarning($"The task: {job.Group} - {job.Name}: {job.Type} is not registered and cannot create an instance of the performer type.");
return Task.FromResult(false);
}
var jobData = job.Args;
jobData[nameof(JobInfo.Id)] = job.Id;
jobData[nameof(JobInfo.Group)] = job.Group;
jobData[nameof(JobInfo.Name)] = job.Name;
switch (job.JobType)
{
case JobType.Once:
var jobId = BackgroundJob.Schedule<HangfireJobSimpleAdapter>(
adapter => adapter.ExecuteAsync(jobType, jobData.ToImmutableDictionary()),
TimeSpan.FromSeconds(job.Interval));
job.Args["hangfire"] = jobId;
break;
case JobType.Persistent:
var minuteInterval = job.Interval / 60;
if (minuteInterval < 1)
{
minuteInterval = 1;
}
RecurringJob.AddOrUpdate<HangfireJobSimpleAdapter>(
adapter => adapter.ExecuteAsync(jobType, jobData.ToImmutableDictionary()),
Cron.MinuteInterval(minuteInterval));
break;
case JobType.Period:
RecurringJob.AddOrUpdate<HangfireJobSimpleAdapter>(
adapter => adapter.ExecuteAsync(jobType, jobData.ToImmutableDictionary()),
job.Cron,
queue: job.Group);
break;
}
return Task.FromResult(true);
}
public Task QueuesAsync(IEnumerable<JobInfo> jobs)
{
throw new NotImplementedException();
}
public Task<bool> RemoveAsync(JobInfo job)
{
throw new NotImplementedException();
}
public Task ResumeAsync(JobInfo job)
{
throw new NotImplementedException();
}
public Task<bool> ShutdownAsync()
{
throw new NotImplementedException();
}
public Task<bool> StartAsync()
{
throw new NotImplementedException();
}
public Task<bool> StopAsync()
{
throw new NotImplementedException();
}
public Task TriggerAsync(JobInfo job)
{
throw new NotImplementedException();
}
}

32
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Hangfire/LINGYUN/Abp/BackgroundTasks/Hangfire/HangfireJobSimpleAdapter.cs

@ -0,0 +1,32 @@
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace LINGYUN.Abp.BackgroundTasks.Hangfire;
public class HangfireJobSimpleAdapter
{
protected IServiceProvider ServiceProvider { get; }
public HangfireJobSimpleAdapter(
IServiceProvider serviceProvider)
{
ServiceProvider = serviceProvider;
}
public async virtual Task<object> ExecuteAsync(Type jobRunnableType, IReadOnlyDictionary<string, object> jobData)
{
using var scope = ServiceProvider.CreateScope();
var jobExecuter = scope.ServiceProvider.GetRequiredService<IJobRunnableExecuter>();
var jobContext = new JobRunnableContext(
jobRunnableType,
ServiceProvider,
jobData);
await jobExecuter.ExecuteAsync(jobContext);
return jobContext.Result;
}
}

36
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobExecutorProvider.cs

@ -48,13 +48,18 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD
jobType = adapterType.MakeGenericType(jobType); jobType = adapterType.MakeGenericType(jobType);
} }
// 改为 JobId作为名称
var jobBuilder = JobBuilder.Create(jobType) var jobBuilder = JobBuilder.Create(jobType)
.WithIdentity(job.Name, job.Group) .WithIdentity(job.Id.ToString(), job.Group)
.WithDescription(job.Description); .WithDescription(job.Description);
jobBuilder.UsingJobData(nameof(JobInfo.Id), job.Id); jobBuilder.UsingJobData(nameof(JobInfo.Id), job.Id);
jobBuilder.UsingJobData(nameof(JobInfo.LockTimeOut), job.LockTimeOut); jobBuilder.UsingJobData(nameof(JobInfo.LockTimeOut), job.LockTimeOut);
jobBuilder.UsingJobData(new JobDataMap(job.Args)); jobBuilder.UsingJobData(new JobDataMap(job.Args));
if (job.TenantId.HasValue)
{
jobBuilder.UsingJobData(nameof(JobInfo.TenantId), job.TenantId.ToString());
}
return jobBuilder.Build(); return jobBuilder.Build();
} }
@ -71,11 +76,16 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD
Logger.LogWarning($"The task: {job.Group} - {job.Name} periodic task Cron expression was invalid and the task trigger could not be created."); Logger.LogWarning($"The task: {job.Group} - {job.Name} periodic task Cron expression was invalid and the task trigger could not be created.");
return null; return null;
} }
if (job.GetCanBeTriggered() == 0)
{
Logger.LogWarning($"The task: {job.Group} - {job.Name} reached trigger peak and the task trigger could not be created.");
return null;
}
triggerBuilder triggerBuilder
.WithIdentity(job.Name, job.Group) .WithIdentity(job.Id.ToString(), job.Group)
.WithDescription(job.Description) .WithDescription(job.Description)
.EndAt(job.EndTime) .EndAt(job.EndTime)
.ForJob(job.Name, job.Group) .ForJob(job.Id.ToString(), job.Group)
.WithPriority((int)job.Priority) .WithPriority((int)job.Priority)
.WithCronSchedule(job.Cron); .WithCronSchedule(job.Cron);
if (job.BeginTime > Clock.Now) if (job.BeginTime > Clock.Now)
@ -86,22 +96,26 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD
case JobType.Once: case JobType.Once:
case JobType.Persistent: case JobType.Persistent:
default: default:
// Quartz 需要减一位 var maxCount = job.GetCanBeTriggered();
var maxCount = job.MaxCount <= 0 ? -1 : job.MaxCount - 1; if (maxCount == 0)
if (job.JobType == JobType.Once)
{ {
maxCount = 0; Logger.LogWarning($"The task: {job.Group} - {job.Name} reached trigger peak and the task trigger could not be created.");
return null;
} }
if (job.Status == JobStatus.FailedRetry && job.TryCount < job.MaxTryCount)
// Quartz 需要减一位
maxCount -= 1;
if (maxCount < -1)
{ {
maxCount = job.MaxTryCount <= 0 ? -1 : job.MaxTryCount - 1; maxCount = -1;
} }
triggerBuilder triggerBuilder
.WithIdentity(job.Name, job.Group) .WithIdentity(job.Id.ToString(), job.Group)
.WithDescription(job.Description) .WithDescription(job.Description)
.StartAt(Clock.Now.AddSeconds(job.Interval)) .StartAt(Clock.Now.AddSeconds(job.Interval))
.EndAt(job.EndTime) .EndAt(job.EndTime)
.ForJob(job.Name, job.Group) .ForJob(job.Id.ToString(), job.Group)
.WithPriority((int)job.Priority) .WithPriority((int)job.Priority)
.WithSimpleSchedule(x => .WithSimpleSchedule(x =>
x.WithIntervalInSeconds(job.Interval) x.WithIntervalInSeconds(job.Interval)

14
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks.Quartz/LINGYUN/Abp/BackgroundTasks/Quartz/QuartzJobScheduler.cs

@ -24,13 +24,13 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
public virtual async Task<bool> ExistsAsync(JobInfo job) public virtual async Task<bool> ExistsAsync(JobInfo job)
{ {
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Id.ToString(), job.Group);
return await Scheduler.CheckExists(jobKey); return await Scheduler.CheckExists(jobKey);
} }
public virtual async Task PauseAsync(JobInfo job) public virtual async Task PauseAsync(JobInfo job)
{ {
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Id.ToString(), job.Group);
if (await Scheduler.CheckExists(jobKey)) if (await Scheduler.CheckExists(jobKey))
{ {
var triggers = await Scheduler.GetTriggersOfJob(jobKey); var triggers = await Scheduler.GetTriggersOfJob(jobKey);
@ -43,7 +43,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
public virtual async Task<bool> QueueAsync(JobInfo job) public virtual async Task<bool> QueueAsync(JobInfo job)
{ {
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Id.ToString(), job.Group);
if (await Scheduler.CheckExists(jobKey)) if (await Scheduler.CheckExists(jobKey))
{ {
return false; return false;
@ -86,12 +86,12 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
jobDictionary[jobDetail] = new ITrigger[] { jobTrigger }; jobDictionary[jobDetail] = new ITrigger[] { jobTrigger };
} }
await Scheduler.ScheduleJobs(jobDictionary, false); await Scheduler.ScheduleJobs(jobDictionary, true);
} }
public virtual async Task<bool> RemoveAsync(JobInfo job) public virtual async Task<bool> RemoveAsync(JobInfo job)
{ {
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Id.ToString(), job.Group);
if (!await Scheduler.CheckExists(jobKey)) if (!await Scheduler.CheckExists(jobKey))
{ {
return false; return false;
@ -109,7 +109,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
public virtual async Task ResumeAsync(JobInfo job) public virtual async Task ResumeAsync(JobInfo job)
{ {
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Id.ToString(), job.Group);
if (await Scheduler.CheckExists(jobKey)) if (await Scheduler.CheckExists(jobKey))
{ {
var triggers = await Scheduler.GetTriggersOfJob(jobKey); var triggers = await Scheduler.GetTriggersOfJob(jobKey);
@ -150,7 +150,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
public virtual async Task TriggerAsync(JobInfo job) public virtual async Task TriggerAsync(JobInfo job)
{ {
var jobKey = new JobKey(job.Name, job.Group); var jobKey = new JobKey(job.Id.ToString(), job.Group);
if (!await Scheduler.CheckExists(jobKey)) if (!await Scheduler.CheckExists(jobKey))
{ {
await QueueAsync(job); await QueueAsync(job);

5
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/BackgroundJobManager.cs

@ -6,6 +6,7 @@ using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids; using Volo.Abp.Guids;
using Volo.Abp.Json; using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing; using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks; namespace LINGYUN.Abp.BackgroundTasks;
@ -16,6 +17,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
protected IClock Clock { get; } protected IClock Clock { get; }
protected IJobStore JobStore { get; } protected IJobStore JobStore { get; }
protected IJobScheduler JobScheduler { get; } protected IJobScheduler JobScheduler { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IGuidGenerator GuidGenerator { get; } protected IGuidGenerator GuidGenerator { get; }
protected IJsonSerializer JsonSerializer { get; } protected IJsonSerializer JsonSerializer { get; }
protected AbpBackgroundJobOptions Options { get; } protected AbpBackgroundJobOptions Options { get; }
@ -23,6 +25,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
IClock clock, IClock clock,
IJobStore jobStore, IJobStore jobStore,
IJobScheduler jobScheduler, IJobScheduler jobScheduler,
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator, IGuidGenerator guidGenerator,
IJsonSerializer jsonSerializer, IJsonSerializer jsonSerializer,
IOptions<AbpBackgroundJobOptions> options) IOptions<AbpBackgroundJobOptions> options)
@ -30,6 +33,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
Clock = clock; Clock = clock;
JobStore = jobStore; JobStore = jobStore;
JobScheduler = jobScheduler; JobScheduler = jobScheduler;
CurrentTenant = currentTenant;
GuidGenerator = guidGenerator; GuidGenerator = guidGenerator;
JsonSerializer = jsonSerializer; JsonSerializer = jsonSerializer;
Options = options.Value; Options = options.Value;
@ -57,6 +61,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
var jobInfo = new JobInfo var jobInfo = new JobInfo
{ {
Id = jobId, Id = jobId,
TenantId = CurrentTenant.Id,
Name = jobId.ToString(), Name = jobId.ToString(),
Group = "BackgroundJobs", Group = "BackgroundJobs",
Priority = ConverForm(priority), Priority = ConverForm(priority),

6
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs

@ -3,6 +3,7 @@ using Microsoft.Extensions.Logging;
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks.Internal; namespace LINGYUN.Abp.BackgroundTasks.Internal;
@ -11,11 +12,15 @@ public class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDepend
protected override async Task OnJobAfterExecutedAsync(JobEventContext context) protected override async Task OnJobAfterExecutedAsync(JobEventContext context)
{ {
var store = context.ServiceProvider.GetRequiredService<IJobStore>(); var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
using (currentTenant.Change(context.EventData.TenantId))
{
var job = await store.FindAsync(context.EventData.Key); var job = await store.FindAsync(context.EventData.Key);
if (job != null) if (job != null)
{ {
job.TriggerCount += 1; job.TriggerCount += 1;
job.TenantId = context.EventData.TenantId;
job.LastRunTime = context.EventData.RunTime; job.LastRunTime = context.EventData.RunTime;
job.NextRunTime = context.EventData.NextRunTime; job.NextRunTime = context.EventData.NextRunTime;
job.Result = context.EventData.Result ?? "OK"; job.Result = context.EventData.Result ?? "OK";
@ -72,6 +77,7 @@ public class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDepend
await store.StoreAsync(job); await store.StoreAsync(job);
} }
} }
}
private async Task RemoveJobAsync(JobEventContext context, JobInfo jobInfo) private async Task RemoveJobAsync(JobEventContext context, JobInfo jobInfo)
{ {

6
aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobLogEvent.cs

@ -2,7 +2,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Auditing; using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow; using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks.Internal; namespace LINGYUN.Abp.BackgroundTasks.Internal;
@ -21,7 +21,11 @@ public class JobLogEvent : JobEventBase<JobLogEvent>, ITransientDependency
return; return;
} }
var store = context.ServiceProvider.GetRequiredService<IJobStore>(); var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
using (currentTenant.Change(context.EventData.TenantId))
{
await store.StoreLogAsync(context.EventData); await store.StoreLogAsync(context.EventData);
} }
} }
}

9
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/BackgroundJobInfoBatchInput.cs

@ -0,0 +1,9 @@
using System;
using System.Collections.Generic;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfoBatchInput
{
public List<Guid> JobIds { get; set; } = new List<Guid>();
}

14
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/IBackgroundJobInfoAppService.cs

@ -19,4 +19,18 @@ public interface IBackgroundJobInfoAppService :
Task ResumeAsync(Guid id); Task ResumeAsync(Guid id);
Task StopAsync(Guid id); Task StopAsync(Guid id);
Task StartAsync(Guid id);
Task BulkDeleteAsync(BackgroundJobInfoBatchInput input);
Task BulkStopAsync(BackgroundJobInfoBatchInput input);
Task BulkStartAsync(BackgroundJobInfoBatchInput input);
Task BulkTriggerAsync(BackgroundJobInfoBatchInput input);
Task BulkResumeAsync(BackgroundJobInfoBatchInput input);
Task BulkPauseAsync(BackgroundJobInfoBatchInput input);
} }

37
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissionDefinitionProvider.cs

@ -1,7 +1,6 @@
using LINGYUN.Abp.TaskManagement.Localization; using LINGYUN.Abp.TaskManagement.Localization;
using Volo.Abp.Authorization.Permissions; using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization; using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.TaskManagement.Permissions; namespace LINGYUN.Abp.TaskManagement.Permissions;
@ -11,50 +10,42 @@ public class TaskManagementPermissionDefinitionProvider : PermissionDefinitionPr
{ {
var group = context.AddGroup( var group = context.AddGroup(
TaskManagementPermissions.GroupName, TaskManagementPermissions.GroupName,
L("Permissions:TaskManagement"), L("Permissions:TaskManagement"));
MultiTenancySides.Host);
var backgroundJobs = group.AddPermission( var backgroundJobs = group.AddPermission(
TaskManagementPermissions.BackgroundJobs.Default, TaskManagementPermissions.BackgroundJobs.Default,
L("Permissions:BackgroundJobs"), L("Permissions:BackgroundJobs"));
MultiTenancySides.Host);
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Create, TaskManagementPermissions.BackgroundJobs.Create,
L("Permissions:CreateJob"), L("Permissions:CreateJob"));
MultiTenancySides.Host);
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Update, TaskManagementPermissions.BackgroundJobs.Update,
L("Permissions:UpdateJob"), L("Permissions:UpdateJob"));
MultiTenancySides.Host);
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Delete, TaskManagementPermissions.BackgroundJobs.Delete,
L("Permissions:DeleteJob"), L("Permissions:DeleteJob"));
MultiTenancySides.Host);
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Trigger, TaskManagementPermissions.BackgroundJobs.Trigger,
L("Permissions:TriggerJob"), L("Permissions:TriggerJob"));
MultiTenancySides.Host);
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Pause, TaskManagementPermissions.BackgroundJobs.Pause,
L("Permissions:PauseJob"), L("Permissions:PauseJob"));
MultiTenancySides.Host);
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Resume, TaskManagementPermissions.BackgroundJobs.Resume,
L("Permissions:ResumeJob"), L("Permissions:ResumeJob"));
MultiTenancySides.Host); backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Start,
L("Permissions:StartJob"));
backgroundJobs.AddChild( backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Stop, TaskManagementPermissions.BackgroundJobs.Stop,
L("Permissions:StopJob"), L("Permissions:StopJob"));
MultiTenancySides.Host);
var backgroundJobLogs = group.AddPermission( var backgroundJobLogs = group.AddPermission(
TaskManagementPermissions.BackgroundJobLogs.Default, TaskManagementPermissions.BackgroundJobLogs.Default,
L("Permissions:BackgroundJobLogs"), L("Permissions:BackgroundJobLogs"));
MultiTenancySides.Host);
backgroundJobLogs.AddChild( backgroundJobLogs.AddChild(
TaskManagementPermissions.BackgroundJobLogs.Delete, TaskManagementPermissions.BackgroundJobLogs.Delete,
L("Permissions:DeleteJobLogs"), L("Permissions:DeleteJobLogs"));
MultiTenancySides.Host);
} }
private ILocalizableString L(string name) private ILocalizableString L(string name)

1
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application.Contracts/LINGYUN/Abp/TaskManagement/Permissions/TaskManagementPermissions.cs

@ -13,6 +13,7 @@ public static class TaskManagementPermissions
public const string Trigger = Default + ".Trigger"; public const string Trigger = Default + ".Trigger";
public const string Pause = Default + ".Pause"; public const string Pause = Default + ".Pause";
public const string Resume = Default + ".Resume"; public const string Resume = Default + ".Resume";
public const string Start = Default + ".Start";
public const string Stop = Default + ".Stop"; public const string Stop = Default + ".Stop";
} }

92
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Application/LINGYUN/Abp/TaskManagement/BackgroundJobInfoAppService.cs

@ -3,6 +3,7 @@ using LINGYUN.Abp.TaskManagement.Permissions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Application.Dtos; using Volo.Abp.Application.Dtos;
@ -44,7 +45,8 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
input.EndTime, input.EndTime,
input.Priority, input.Priority,
input.MaxCount, input.MaxCount,
input.MaxTryCount); input.MaxTryCount,
CurrentTenant.Id);
UpdateByInput(backgroundJobInfo, input); UpdateByInput(backgroundJobInfo, input);
@ -132,6 +134,14 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
await BackgroundJobManager.StopAsync(backgroundJobInfo); await BackgroundJobManager.StopAsync(backgroundJobInfo);
} }
[Authorize(TaskManagementPermissions.BackgroundJobs.Start)]
public virtual async Task StartAsync(Guid id)
{
var backgroundJobInfo = await BackgroundJobInfoRepository.GetAsync(id);
await BackgroundJobManager.QueueAsync(backgroundJobInfo);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Update)] [Authorize(TaskManagementPermissions.BackgroundJobs.Update)]
public virtual async Task<BackgroundJobInfoDto> UpdateAsync(Guid id, BackgroundJobInfoUpdateDto input) public virtual async Task<BackgroundJobInfoDto> UpdateAsync(Guid id, BackgroundJobInfoUpdateDto input)
{ {
@ -148,6 +158,86 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
return ObjectMapper.Map<BackgroundJobInfo, BackgroundJobInfoDto>(backgroundJobInfo); return ObjectMapper.Map<BackgroundJobInfo, BackgroundJobInfoDto>(backgroundJobInfo);
} }
[Authorize(TaskManagementPermissions.BackgroundJobs.Delete)]
public virtual async Task BulkDeleteAsync(BackgroundJobInfoBatchInput input)
{
if (!input.JobIds.Any())
{
return;
}
var jobs = await GetListAsync(input);
await BackgroundJobManager.BulkDeleteAsync(jobs);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Stop)]
public virtual async Task BulkStopAsync(BackgroundJobInfoBatchInput input)
{
if (!input.JobIds.Any())
{
return;
}
var jobs = await GetListAsync(input);
await BackgroundJobManager.BulkStopAsync(jobs);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Start)]
public virtual async Task BulkStartAsync(BackgroundJobInfoBatchInput input)
{
if (!input.JobIds.Any())
{
return;
}
var jobs = await GetListAsync(input);
await BackgroundJobManager.BulkQueueAsync(jobs);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Trigger)]
public virtual async Task BulkTriggerAsync(BackgroundJobInfoBatchInput input)
{
if (!input.JobIds.Any())
{
return;
}
var jobs = await GetListAsync(input);
await BackgroundJobManager.BulkTriggerAsync(jobs);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Resume)]
public virtual async Task BulkResumeAsync(BackgroundJobInfoBatchInput input)
{
if (!input.JobIds.Any())
{
return;
}
var jobs = await GetListAsync(input);
await BackgroundJobManager.BulkResumeAsync(jobs);
}
[Authorize(TaskManagementPermissions.BackgroundJobs.Pause)]
public virtual async Task BulkPauseAsync(BackgroundJobInfoBatchInput input)
{
if (!input.JobIds.Any())
{
return;
}
var jobs = await GetListAsync(input);
await BackgroundJobManager.BulkPauseAsync(jobs);
}
protected virtual async Task<IEnumerable<BackgroundJobInfo>> GetListAsync(BackgroundJobInfoBatchInput input)
{
var quaryble = await BackgroundJobInfoRepository.GetQueryableAsync();
quaryble = quaryble.Where(x => input.JobIds.Contains(x.Id));
return await AsyncExecuter.ToListAsync(quaryble);
}
protected virtual void UpdateByInput(BackgroundJobInfo backgroundJobInfo, BackgroundJobInfoCreateOrUpdateDto input) protected virtual void UpdateByInput(BackgroundJobInfo backgroundJobInfo, BackgroundJobInfoCreateOrUpdateDto input)
{ {
backgroundJobInfo.IsEnabled = input.IsEnabled; backgroundJobInfo.IsEnabled = input.IsEnabled;

8
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/en.json

@ -9,6 +9,7 @@
"Permissions:TriggerJob": "Trigger Job", "Permissions:TriggerJob": "Trigger Job",
"Permissions:PauseJob": "Pause Job", "Permissions:PauseJob": "Pause Job",
"Permissions:ResumeJob": "Resume Job", "Permissions:ResumeJob": "Resume Job",
"Permissions:StartJob": "Start Job",
"Permissions:StopJob": "Stop Job", "Permissions:StopJob": "Stop Job",
"Permissions:BackgroundJobLogs": "BackgroundJobs Logs", "Permissions:BackgroundJobLogs": "BackgroundJobs Logs",
"Permissions:DeleteJobLogs": "Delete Job Logs", "Permissions:DeleteJobLogs": "Delete Job Logs",
@ -72,8 +73,11 @@
"BackgroundJobs:Pause": "Pause", "BackgroundJobs:Pause": "Pause",
"BackgroundJobs:Resume": "Resume", "BackgroundJobs:Resume": "Resume",
"BackgroundJobs:Trigger": "Trigger", "BackgroundJobs:Trigger": "Trigger",
"BackgroundJobs:Stop": "Stop", "BackgroundJobs:Start": "Start Jobs",
"BackgroundJobs:Stop": "Stop Jobs",
"BackgroundJobs:Delete": "Delete",
"BasicInfo": "Basic", "BasicInfo": "Basic",
"Paramters": "Paramters" "Paramters": "Paramters",
"MultipleSelectJobsWillBeDeletedMessage": "Multiple jobs selected will be deleted!"
} }
} }

8
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain.Shared/LINGYUN/Abp/TaskManagement/Localization/Resources/zh-Hans.json

@ -9,6 +9,7 @@
"Permissions:TriggerJob": "触发作业", "Permissions:TriggerJob": "触发作业",
"Permissions:PauseJob": "暂停作业", "Permissions:PauseJob": "暂停作业",
"Permissions:ResumeJob": "恢复作业", "Permissions:ResumeJob": "恢复作业",
"Permissions:StartJob": "启动作业",
"Permissions:StopJob": "停止作业", "Permissions:StopJob": "停止作业",
"Permissions:BackgroundJobLogs": "日志管理", "Permissions:BackgroundJobLogs": "日志管理",
"Permissions:DeleteJobLogs": "删除作业日志", "Permissions:DeleteJobLogs": "删除作业日志",
@ -72,8 +73,11 @@
"BackgroundJobs:Pause": "暂停", "BackgroundJobs:Pause": "暂停",
"BackgroundJobs:Resume": "恢复", "BackgroundJobs:Resume": "恢复",
"BackgroundJobs:Trigger": "触发", "BackgroundJobs:Trigger": "触发",
"BackgroundJobs:Stop": "停止", "BackgroundJobs:Start": "启动作业",
"BackgroundJobs:Stop": "停止作业",
"BackgroundJobs:Delete": "删除",
"BasicInfo": "基本信息", "BasicInfo": "基本信息",
"Paramters": "参数列表" "Paramters": "参数列表",
"MultipleSelectJobsWillBeDeletedMessage": "选择的多个作业将被删除!"
} }
} }

8
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobInfo.cs

@ -4,11 +4,13 @@ using System.Collections.Generic;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Data; using Volo.Abp.Data;
using Volo.Abp.Domain.Entities.Auditing; using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.TaskManagement; namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfo : AuditedAggregateRoot<Guid> public class BackgroundJobInfo : AuditedAggregateRoot<Guid>, IMultiTenant
{ {
public virtual Guid? TenantId { get; protected set; }
/// <summary> /// <summary>
/// 任务名称 /// 任务名称
/// </summary> /// </summary>
@ -114,7 +116,8 @@ public class BackgroundJobInfo : AuditedAggregateRoot<Guid>
DateTime? endTime = null, DateTime? endTime = null,
JobPriority priority = JobPriority.Normal, JobPriority priority = JobPriority.Normal,
int maxCount = 0, int maxCount = 0,
int maxTryCount = 50) : base(id) int maxTryCount = 50,
Guid? tenantId = null) : base(id)
{ {
Name = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength); Name = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength);
Group = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength); Group = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength);
@ -125,6 +128,7 @@ public class BackgroundJobInfo : AuditedAggregateRoot<Guid>
MaxCount = maxCount; MaxCount = maxCount;
MaxTryCount = maxTryCount; MaxTryCount = maxTryCount;
TenantId = tenantId;
Status = JobStatus.Running; Status = JobStatus.Running;

8
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobLog.cs

@ -1,11 +1,13 @@
using System; using System;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.TaskManagement; namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobLog : Entity<long> public class BackgroundJobLog : Entity<long>, IMultiTenant
{ {
public virtual Guid? TenantId { get; protected set; }
public virtual Guid? JobId { get; set; } public virtual Guid? JobId { get; set; }
public virtual string JobName { get; protected set; } public virtual string JobName { get; protected set; }
public virtual string JobGroup { get; protected set; } public virtual string JobGroup { get; protected set; }
@ -18,12 +20,14 @@ public class BackgroundJobLog : Entity<long>
string type, string type,
string group, string group,
string name, string name,
DateTime runTime) DateTime runTime,
Guid? tenantId = null)
{ {
JobType = Check.NotNullOrWhiteSpace(type, nameof(type), BackgroundJobInfoConsts.MaxTypeLength); JobType = Check.NotNullOrWhiteSpace(type, nameof(type), BackgroundJobInfoConsts.MaxTypeLength);
JobGroup = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength); JobGroup = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength);
JobName = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength); JobName = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength);
RunTime = runTime; RunTime = runTime;
TenantId = tenantId;
} }
public BackgroundJobLog SetMessage(string message, Exception ex) public BackgroundJobLog SetMessage(string message, Exception ex)

47
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobManager.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.BackgroundTasks; using LINGYUN.Abp.BackgroundTasks;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Collections.Generic;
using Volo.Abp; using Volo.Abp;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Volo.Abp.ObjectMapping; using Volo.Abp.ObjectMapping;
@ -83,12 +84,26 @@ public class BackgroundJobManager : DomainService
}); });
} }
public virtual async Task BulkDeleteAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
foreach (var jobInfo in jobInfos)
{
await DeleteAsync(jobInfo);
}
}
public virtual async Task QueueAsync(BackgroundJobInfo jobInfo) public virtual async Task QueueAsync(BackgroundJobInfo jobInfo)
{ {
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo); var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
await JobScheduler.QueueAsync(job); await JobScheduler.QueueAsync(job);
} }
public virtual async Task BulkQueueAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
var jobs = ObjectMapper.Map<IEnumerable<BackgroundJobInfo>, List<JobInfo>>(jobInfos);
await JobScheduler.QueuesAsync(jobs);
}
public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo) public virtual async Task TriggerAsync(BackgroundJobInfo jobInfo)
{ {
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo); var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -99,6 +114,14 @@ public class BackgroundJobManager : DomainService
await JobScheduler.TriggerAsync(job); await JobScheduler.TriggerAsync(job);
} }
public virtual async Task BulkTriggerAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
foreach (var jobInfo in jobInfos)
{
await TriggerAsync(jobInfo);
}
}
public virtual async Task PauseAsync(BackgroundJobInfo jobInfo) public virtual async Task PauseAsync(BackgroundJobInfo jobInfo)
{ {
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo); var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -110,6 +133,14 @@ public class BackgroundJobManager : DomainService
await BackgroundJobInfoRepository.UpdateAsync(jobInfo); await BackgroundJobInfoRepository.UpdateAsync(jobInfo);
} }
public virtual async Task BulkPauseAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
foreach (var jobInfo in jobInfos)
{
await PauseAsync(jobInfo);
}
}
public virtual async Task ResumeAsync(BackgroundJobInfo jobInfo) public virtual async Task ResumeAsync(BackgroundJobInfo jobInfo)
{ {
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo); var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -122,6 +153,14 @@ public class BackgroundJobManager : DomainService
await BackgroundJobInfoRepository.UpdateAsync(jobInfo); await BackgroundJobInfoRepository.UpdateAsync(jobInfo);
} }
public virtual async Task BulkResumeAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
foreach (var jobInfo in jobInfos)
{
await ResumeAsync(jobInfo);
}
}
public virtual async Task StopAsync(BackgroundJobInfo jobInfo) public virtual async Task StopAsync(BackgroundJobInfo jobInfo)
{ {
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo); var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -132,4 +171,12 @@ public class BackgroundJobManager : DomainService
await BackgroundJobInfoRepository.UpdateAsync(jobInfo); await BackgroundJobInfoRepository.UpdateAsync(jobInfo);
} }
public virtual async Task BulkStopAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
foreach (var jobInfo in jobInfos)
{
await StopAsync(jobInfo);
}
}
} }

23
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.Domain/LINGYUN/Abp/TaskManagement/BackgroundJobStore.cs

@ -3,7 +3,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping; using Volo.Abp.ObjectMapping;
using Volo.Abp.Uow; using Volo.Abp.Uow;
@ -12,33 +14,45 @@ namespace LINGYUN.Abp.TaskManagement;
[Dependency(ReplaceServices = true)] [Dependency(ReplaceServices = true)]
public class BackgroundJobStore : IJobStore, ITransientDependency public class BackgroundJobStore : IJobStore, ITransientDependency
{ {
protected IDataFilter DataFilter { get; }
protected IObjectMapper ObjectMapper { get; } protected IObjectMapper ObjectMapper { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IBackgroundJobInfoRepository JobInfoRepository { get; } protected IBackgroundJobInfoRepository JobInfoRepository { get; }
protected IBackgroundJobLogRepository JobLogRepository { get; } protected IBackgroundJobLogRepository JobLogRepository { get; }
public BackgroundJobStore( public BackgroundJobStore(
IDataFilter dataFilter,
IObjectMapper objectMapper, IObjectMapper objectMapper,
ICurrentTenant currentTenant,
IBackgroundJobInfoRepository jobInfoRepository, IBackgroundJobInfoRepository jobInfoRepository,
IBackgroundJobLogRepository jobLogRepository) IBackgroundJobLogRepository jobLogRepository)
{ {
DataFilter = dataFilter;
ObjectMapper = objectMapper; ObjectMapper = objectMapper;
CurrentTenant = currentTenant;
JobInfoRepository = jobInfoRepository; JobInfoRepository = jobInfoRepository;
JobLogRepository = jobLogRepository; JobLogRepository = jobLogRepository;
} }
public async virtual Task<List<JobInfo>> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default) public async virtual Task<List<JobInfo>> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default)
{
using (DataFilter.Disable<IMultiTenant>())
{ {
var jobInfos = await JobInfoRepository.GetAllPeriodTasksAsync(cancellationToken); var jobInfos = await JobInfoRepository.GetAllPeriodTasksAsync(cancellationToken);
return ObjectMapper.Map<List<BackgroundJobInfo>, List<JobInfo>>(jobInfos); return ObjectMapper.Map<List<BackgroundJobInfo>, List<JobInfo>>(jobInfos);
} }
}
public async virtual Task<List<JobInfo>> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default) public async virtual Task<List<JobInfo>> GetWaitingListAsync(int maxResultCount, CancellationToken cancellationToken = default)
{
using (DataFilter.Disable<IMultiTenant>())
{ {
var jobInfos = await JobInfoRepository.GetWaitingListAsync(maxResultCount, cancellationToken); var jobInfos = await JobInfoRepository.GetWaitingListAsync(maxResultCount, cancellationToken);
return ObjectMapper.Map<List<BackgroundJobInfo>, List<JobInfo>>(jobInfos); return ObjectMapper.Map<List<BackgroundJobInfo>, List<JobInfo>>(jobInfos);
} }
}
public async virtual Task<JobInfo> FindAsync(Guid jobId) public async virtual Task<JobInfo> FindAsync(Guid jobId)
{ {
@ -49,6 +63,8 @@ public class BackgroundJobStore : IJobStore, ITransientDependency
[UnitOfWork] [UnitOfWork]
public async virtual Task StoreAsync(JobInfo jobInfo) public async virtual Task StoreAsync(JobInfo jobInfo)
{
using (CurrentTenant.Change(jobInfo.TenantId))
{ {
var backgroundJobInfo = await JobInfoRepository.FindAsync(jobInfo.Id); var backgroundJobInfo = await JobInfoRepository.FindAsync(jobInfo.Id);
if (backgroundJobInfo != null) if (backgroundJobInfo != null)
@ -104,9 +120,12 @@ public class BackgroundJobStore : IJobStore, ITransientDependency
await JobInfoRepository.InsertAsync(backgroundJobInfo); await JobInfoRepository.InsertAsync(backgroundJobInfo);
} }
} }
}
[UnitOfWork] [UnitOfWork]
public async virtual Task StoreLogAsync(JobEventData eventData) public async virtual Task StoreLogAsync(JobEventData eventData)
{
using (CurrentTenant.Change(eventData.TenantId))
{ {
var jogLog = new BackgroundJobLog( var jogLog = new BackgroundJobLog(
eventData.Type.Name, eventData.Type.Name,
@ -123,12 +142,15 @@ public class BackgroundJobStore : IJobStore, ITransientDependency
await JobLogRepository.InsertAsync(jogLog); await JobLogRepository.InsertAsync(jogLog);
} }
}
[UnitOfWork] [UnitOfWork]
public async virtual Task CleanupAsync( public async virtual Task CleanupAsync(
int maxResultCount, int maxResultCount,
TimeSpan jobExpiratime, TimeSpan jobExpiratime,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{
using (DataFilter.Disable<IMultiTenant>())
{ {
var jobs = await JobInfoRepository.GetExpiredJobsAsync( var jobs = await JobInfoRepository.GetExpiredJobsAsync(
maxResultCount, maxResultCount,
@ -138,3 +160,4 @@ public class BackgroundJobStore : IJobStore, ITransientDependency
await JobInfoRepository.DeleteManyAsync(jobs, cancellationToken: cancellationToken); await JobInfoRepository.DeleteManyAsync(jobs, cancellationToken: cancellationToken);
} }
} }
}

2
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.EntityFrameworkCore/LINGYUN/Abp/TaskManagement/EntityFrameworkCore/EfCoreBackgroundJobInfoRepository.cs

@ -102,7 +102,7 @@ public class EfCoreBackgroundJobInfoRepository :
.WhereIf(filter.EndTime.HasValue, x => filter.EndTime.Value.CompareTo(x.EndTime) >= 0) .WhereIf(filter.EndTime.HasValue, x => filter.EndTime.Value.CompareTo(x.EndTime) >= 0)
.WhereIf(filter.BeginCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.BeginCreationTime.Value) >= 0) .WhereIf(filter.BeginCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.BeginCreationTime.Value) >= 0)
.WhereIf(filter.EndCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.EndCreationTime.Value) <= 0) .WhereIf(filter.EndCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.EndCreationTime.Value) <= 0)
.OrderBy(sorting ?? nameof(BackgroundJobInfo.CreationTime)) .OrderBy(sorting ?? $"{nameof(BackgroundJobInfo.CreationTime)} DESC")
.PageBy(skipCount, maxResultCount) .PageBy(skipCount, maxResultCount)
.ToListAsync(GetCancellationToken(cancellationToken)); .ToListAsync(GetCancellationToken(cancellationToken));
} }

55
aspnet-core/modules/task-management/LINGYUN.Abp.TaskManagement.HttpApi/LINGYUN/Abp/TaskManagement/BackgroundJobInfoController.cs

@ -89,4 +89,59 @@ public class BackgroundJobInfoController : TaskManagementController, IBackground
{ {
return BackgroundJobInfoAppService.UpdateAsync(id, input); return BackgroundJobInfoAppService.UpdateAsync(id, input);
} }
[HttpPut]
[Route("{id}/start")]
[Authorize(TaskManagementPermissions.BackgroundJobs.Start)]
public Task StartAsync(Guid id)
{
return BackgroundJobInfoAppService.StartAsync(id);
}
[HttpPut]
[Route("bulk-stop")]
[Authorize(TaskManagementPermissions.BackgroundJobs.Stop)]
public Task BulkStopAsync(BackgroundJobInfoBatchInput input)
{
return BackgroundJobInfoAppService.BulkStopAsync(input);
}
[HttpPut]
[Route("bulk-start")]
[Authorize(TaskManagementPermissions.BackgroundJobs.Start)]
public Task BulkStartAsync(BackgroundJobInfoBatchInput input)
{
return BackgroundJobInfoAppService.BulkStartAsync(input);
}
[HttpPut]
[Route("bulk-trigger")]
[Authorize(TaskManagementPermissions.BackgroundJobs.Trigger)]
public Task BulkTriggerAsync(BackgroundJobInfoBatchInput input)
{
return BackgroundJobInfoAppService.BulkTriggerAsync(input);
}
[HttpPut]
[Route("bulk-resume")]
[Authorize(TaskManagementPermissions.BackgroundJobs.Resume)]
public Task BulkResumeAsync(BackgroundJobInfoBatchInput input)
{
return BackgroundJobInfoAppService.BulkResumeAsync(input);
}
[HttpPut]
[Route("bulk-pause")]
[Authorize(TaskManagementPermissions.BackgroundJobs.Pause)]
public Task BulkPauseAsync(BackgroundJobInfoBatchInput input)
{
return BackgroundJobInfoAppService.BulkPauseAsync(input);
}
[HttpPut]
[Route("bulk-delete")]
public Task BulkDeleteAsync(BackgroundJobInfoBatchInput input)
{
return BackgroundJobInfoAppService.BulkDeleteAsync(input);
}
} }

201
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220112093435_Add-Support-Multi-Tenancy-With-Background-Jobs.Designer.cs

@ -0,0 +1,201 @@
// <auto-generated />
using System;
using LY.MicroService.TaskManagement.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Volo.Abp.EntityFrameworkCore;
#nullable disable
namespace LY.MicroService.TaskManagement.Migrations
{
[DbContext(typeof(TaskManagementMigrationsDbContext))]
[Migration("20220112093435_Add-Support-Multi-Tenancy-With-Background-Jobs")]
partial class AddSupportMultiTenancyWithBackgroundJobs
{
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("_Abp_DatabaseProvider", EfCoreDatabaseProvider.MySql)
.HasAnnotation("ProductVersion", "6.0.1")
.HasAnnotation("Relational:MaxIdentifierLength", 64);
modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobInfo", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("char(36)");
b.Property<string>("Args")
.HasColumnType("longtext")
.HasColumnName("Args");
b.Property<DateTime>("BeginTime")
.HasColumnType("datetime(6)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasMaxLength(40)
.HasColumnType("varchar(40)")
.HasColumnName("ConcurrencyStamp");
b.Property<DateTime>("CreationTime")
.HasColumnType("datetime(6)")
.HasColumnName("CreationTime");
b.Property<Guid?>("CreatorId")
.HasColumnType("char(36)")
.HasColumnName("CreatorId");
b.Property<string>("Cron")
.HasMaxLength(50)
.HasColumnType("varchar(50)")
.HasColumnName("Cron");
b.Property<string>("Description")
.HasMaxLength(255)
.HasColumnType("varchar(255)")
.HasColumnName("Description");
b.Property<DateTime?>("EndTime")
.HasColumnType("datetime(6)");
b.Property<string>("ExtraProperties")
.HasColumnType("longtext")
.HasColumnName("ExtraProperties");
b.Property<string>("Group")
.IsRequired()
.HasMaxLength(50)
.HasColumnType("varchar(50)")
.HasColumnName("Group");
b.Property<int>("Interval")
.HasColumnType("int");
b.Property<bool>("IsAbandoned")
.HasColumnType("tinyint(1)");
b.Property<bool>("IsEnabled")
.HasColumnType("tinyint(1)");
b.Property<int>("JobType")
.HasColumnType("int");
b.Property<DateTime?>("LastModificationTime")
.HasColumnType("datetime(6)")
.HasColumnName("LastModificationTime");
b.Property<Guid?>("LastModifierId")
.HasColumnType("char(36)")
.HasColumnName("LastModifierId");
b.Property<DateTime?>("LastRunTime")
.HasColumnType("datetime(6)");
b.Property<int>("LockTimeOut")
.HasColumnType("int");
b.Property<int>("MaxCount")
.HasColumnType("int");
b.Property<int>("MaxTryCount")
.HasColumnType("int");
b.Property<string>("Name")
.IsRequired()
.HasMaxLength(100)
.HasColumnType("varchar(100)")
.HasColumnName("Name");
b.Property<DateTime?>("NextRunTime")
.HasColumnType("datetime(6)");
b.Property<int>("Priority")
.HasColumnType("int");
b.Property<string>("Result")
.HasMaxLength(1000)
.HasColumnType("varchar(1000)")
.HasColumnName("Result");
b.Property<int>("Status")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<int>("TriggerCount")
.HasColumnType("int");
b.Property<int>("TryCount")
.HasColumnType("int");
b.Property<string>("Type")
.IsRequired()
.HasMaxLength(1000)
.HasColumnType("varchar(1000)")
.HasColumnName("Type");
b.HasKey("Id");
b.HasIndex("Name", "Group");
b.ToTable("TK_BackgroundJobs", (string)null);
});
modelBuilder.Entity("LINGYUN.Abp.TaskManagement.BackgroundJobLog", b =>
{
b.Property<long>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("bigint");
b.Property<string>("Exception")
.HasMaxLength(2000)
.HasColumnType("varchar(2000)")
.HasColumnName("Exception");
b.Property<string>("JobGroup")
.HasMaxLength(50)
.HasColumnType("varchar(50)")
.HasColumnName("JobGroup");
b.Property<Guid?>("JobId")
.HasColumnType("char(36)");
b.Property<string>("JobName")
.HasMaxLength(100)
.HasColumnType("varchar(100)")
.HasColumnName("JobName");
b.Property<string>("JobType")
.HasMaxLength(1000)
.HasColumnType("varchar(1000)")
.HasColumnName("JobType");
b.Property<string>("Message")
.HasMaxLength(1000)
.HasColumnType("varchar(1000)")
.HasColumnName("Message");
b.Property<DateTime>("RunTime")
.HasColumnType("datetime(6)");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("JobGroup", "JobName");
b.ToTable("TK_BackgroundJobLogs", (string)null);
});
#pragma warning restore 612, 618
}
}
}

38
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/20220112093435_Add-Support-Multi-Tenancy-With-Background-Jobs.cs

@ -0,0 +1,38 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace LY.MicroService.TaskManagement.Migrations
{
public partial class AddSupportMultiTenancyWithBackgroundJobs : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<Guid>(
name: "TenantId",
table: "TK_BackgroundJobs",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
migrationBuilder.AddColumn<Guid>(
name: "TenantId",
table: "TK_BackgroundJobLogs",
type: "char(36)",
nullable: true,
collation: "ascii_general_ci");
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "TenantId",
table: "TK_BackgroundJobs");
migrationBuilder.DropColumn(
name: "TenantId",
table: "TK_BackgroundJobLogs");
}
}
}

8
aspnet-core/services/LY.MicroService.TaskManagement.HttpApi.Host/Migrations/TaskManagementMigrationsDbContextModelSnapshot.cs

@ -123,6 +123,10 @@ namespace LY.MicroService.TaskManagement.Migrations
b.Property<int>("Status") b.Property<int>("Status")
.HasColumnType("int"); .HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<int>("TriggerCount") b.Property<int>("TriggerCount")
.HasColumnType("int"); .HasColumnType("int");
@ -179,6 +183,10 @@ namespace LY.MicroService.TaskManagement.Migrations
b.Property<DateTime>("RunTime") b.Property<DateTime>("RunTime")
.HasColumnType("datetime(6)"); .HasColumnType("datetime(6)");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id"); b.HasKey("Id");
b.HasIndex("JobGroup", "JobName"); b.HasIndex("JobGroup", "JobName");

Loading…
Cancel
Save