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. 12
      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. 98
      aspnet-core/modules/task-management/LINGYUN.Abp.BackgroundTasks/LINGYUN/Abp/BackgroundTasks/Internal/JobExecutedEvent.cs
  21. 8
      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. 155
      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 {
id: string;
changeTime?: Date;
changeType?: ChangeType;
changeType: ChangeType;
entityTenantId?: string;
entityId?: 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',
Resume = '/api/task-management/background-jobs/{id}/resume',
Trigger = '/api/task-management/background-jobs/{id}/trigger',
Start = '/api/task-management/background-jobs/{id}/start',
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) => {
@ -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) => {
return defAbpHttp.put<void>({
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"
:min-height="600"
@ok="handleSubmit"
@visible-change="handleVisibleChange"
>
<Row>
<Col :span="24">
@ -35,7 +36,6 @@
<BasicTree
:checkable="true"
:checkStrictly="true"
:selectable="false"
:disabled="permissionTreeDisabled"
:treeData="permission.children"
:replaceFields="{
@ -104,10 +104,28 @@
} = usePermissions({
getPropsRef: model,
});
const [registerModal, { closeModal, setModalProps }] = useModalInner((val) => {
const [registerModal, { closeModal, changeOkLoading }] = useModalInner((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 {
L,
activeKey,
@ -119,36 +137,12 @@
permissionTreeCheckState,
permissionTreeDisabled,
handlePermissionGranted,
handleSavePermission,
handleGrantAllPermission,
handleGrantPermissions,
registerModal,
closeModal,
setModalProps,
handleSubmit,
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>

12
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 { 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 { PermissionProvider } from '/@/api/permission-management/model/permissionModel';
import {
@ -24,7 +24,7 @@ interface UsePermission {
}
export function usePermissions({ getPropsRef }: UsePermission) {
const { t } = useI18n();
const { L } = useLocalization('AbpPermissionManagement');
/** 弹出层标题 */
const title = ref('');
/** 权限树 */
@ -36,7 +36,7 @@ export function usePermissions({ getPropsRef }: UsePermission) {
*/
function handleGetPermission(name: string, key?: string) {
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);
});
}
@ -145,8 +145,10 @@ export function usePermissions({ getPropsRef }: UsePermission) {
() => unref(getPropsRef).providerKey,
(key) => {
permissionTree.value = [];
const props = unref(getPropsRef);
handleGetPermission(props.providerName, key);
if (key) {
const props = unref(getPropsRef);
handleGetPermission(props.providerName, key);
}
},
);

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

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

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

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

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

@ -12,7 +12,6 @@ import {
Auth,
CurrentUser,
Localization,
Setting,
} from '/@/api/abp/model/appModel';
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',
};
const entityChangeTypeColor = computed(() => {
return (changeType?: ChangeType) => (changeType ? changeTypeColorMap[changeType].color : '');
return (changeType: ChangeType) => changeTypeColorMap[changeType].color;
});
const entityChangeType = computed(() => {
return (changeType?: ChangeType) => (changeType ? changeTypeColorMap[changeType].value : '');
return (changeType: ChangeType) => changeTypeColorMap[changeType].value;
});
const httpMethodColor = computed(() => {
return (method?: string) => {

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

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

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

@ -18,6 +18,7 @@ export function getDataColumns(): BasicColumn[] {
align: 'left',
width: 150,
sorter: true,
fixed: 'left',
},
{
title: L('DisplayName:Name'),
@ -25,10 +26,11 @@ export function getDataColumns(): BasicColumn[] {
align: 'left',
width: 300,
sorter: true,
fixed: 'left',
},
{
title: L('DisplayName:Type'),
dataIndex: 'type',
title: L('DisplayName:Description'),
dataIndex: 'description',
align: 'left',
width: 350,
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>
public Guid Id { get; set; }
/// <summary>
/// 租户标识
/// </summary>
public Guid? TenantId { get; set; }
/// <summary>
/// 任务名称
/// </summary>
public string Name { get; set; }
@ -101,4 +105,39 @@ public class JobInfo
/// 0或更小不生效
/// </summary>
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);
}
// 改为 JobId作为名称
var jobBuilder = JobBuilder.Create(jobType)
.WithIdentity(job.Name, job.Group)
.WithIdentity(job.Id.ToString(), job.Group)
.WithDescription(job.Description);
jobBuilder.UsingJobData(nameof(JobInfo.Id), job.Id);
jobBuilder.UsingJobData(nameof(JobInfo.LockTimeOut), job.LockTimeOut);
jobBuilder.UsingJobData(new JobDataMap(job.Args));
if (job.TenantId.HasValue)
{
jobBuilder.UsingJobData(nameof(JobInfo.TenantId), job.TenantId.ToString());
}
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.");
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
.WithIdentity(job.Name, job.Group)
.WithIdentity(job.Id.ToString(), job.Group)
.WithDescription(job.Description)
.EndAt(job.EndTime)
.ForJob(job.Name, job.Group)
.ForJob(job.Id.ToString(), job.Group)
.WithPriority((int)job.Priority)
.WithCronSchedule(job.Cron);
if (job.BeginTime > Clock.Now)
@ -86,22 +96,26 @@ public class QuartzJobExecutorProvider : IQuartzJobExecutorProvider, ISingletonD
case JobType.Once:
case JobType.Persistent:
default:
// Quartz 需要减一位
var maxCount = job.MaxCount <= 0 ? -1 : job.MaxCount - 1;
if (job.JobType == JobType.Once)
var maxCount = job.GetCanBeTriggered();
if (maxCount == 0)
{
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
.WithIdentity(job.Name, job.Group)
.WithIdentity(job.Id.ToString(), job.Group)
.WithDescription(job.Description)
.StartAt(Clock.Now.AddSeconds(job.Interval))
.EndAt(job.EndTime)
.ForJob(job.Name, job.Group)
.ForJob(job.Id.ToString(), job.Group)
.WithPriority((int)job.Priority)
.WithSimpleSchedule(x =>
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)
{
var jobKey = new JobKey(job.Name, job.Group);
var jobKey = new JobKey(job.Id.ToString(), job.Group);
return await Scheduler.CheckExists(jobKey);
}
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))
{
var triggers = await Scheduler.GetTriggersOfJob(jobKey);
@ -43,7 +43,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
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))
{
return false;
@ -86,12 +86,12 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
jobDictionary[jobDetail] = new ITrigger[] { jobTrigger };
}
await Scheduler.ScheduleJobs(jobDictionary, false);
await Scheduler.ScheduleJobs(jobDictionary, true);
}
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))
{
return false;
@ -109,7 +109,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
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))
{
var triggers = await Scheduler.GetTriggersOfJob(jobKey);
@ -150,7 +150,7 @@ public class QuartzJobScheduler : IJobScheduler, ISingletonDependency
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))
{
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.Guids;
using Volo.Abp.Json;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Timing;
namespace LINGYUN.Abp.BackgroundTasks;
@ -16,6 +17,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
protected IClock Clock { get; }
protected IJobStore JobStore { get; }
protected IJobScheduler JobScheduler { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IGuidGenerator GuidGenerator { get; }
protected IJsonSerializer JsonSerializer { get; }
protected AbpBackgroundJobOptions Options { get; }
@ -23,6 +25,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
IClock clock,
IJobStore jobStore,
IJobScheduler jobScheduler,
ICurrentTenant currentTenant,
IGuidGenerator guidGenerator,
IJsonSerializer jsonSerializer,
IOptions<AbpBackgroundJobOptions> options)
@ -30,6 +33,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
Clock = clock;
JobStore = jobStore;
JobScheduler = jobScheduler;
CurrentTenant = currentTenant;
GuidGenerator = guidGenerator;
JsonSerializer = jsonSerializer;
Options = options.Value;
@ -57,6 +61,7 @@ public class BackgroundJobManager : IBackgroundJobManager, ITransientDependency
var jobInfo = new JobInfo
{
Id = jobId,
TenantId = CurrentTenant.Id,
Name = jobId.ToString(),
Group = "BackgroundJobs",
Priority = ConverForm(priority),

98
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.Threading.Tasks;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
@ -11,65 +12,70 @@ public class JobExecutedEvent : JobEventBase<JobExecutedEvent>, ITransientDepend
protected override async Task OnJobAfterExecutedAsync(JobEventContext context)
{
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
var job = await store.FindAsync(context.EventData.Key);
if (job != null)
using (currentTenant.Change(context.EventData.TenantId))
{
job.TriggerCount += 1;
job.LastRunTime = context.EventData.RunTime;
job.NextRunTime = context.EventData.NextRunTime;
job.Result = context.EventData.Result ?? "OK";
job.Status = JobStatus.Running;
// 一次性任务执行一次后标记为已完成
if (job.JobType == JobType.Once)
{
job.Status = JobStatus.Completed;
}
// 任务异常后可重试
if (context.EventData.Exception != null)
var job = await store.FindAsync(context.EventData.Key);
if (job != null)
{
job.TryCount += 1;
job.IsAbandoned = false;
// 将任务标记为运行中, 会被轮询重新进入队列
job.Status = JobStatus.FailedRetry;
job.Result = GetExceptionMessage(context.EventData.Exception);
job.TriggerCount += 1;
job.TenantId = context.EventData.TenantId;
job.LastRunTime = context.EventData.RunTime;
job.NextRunTime = context.EventData.NextRunTime;
job.Result = context.EventData.Result ?? "OK";
job.Status = JobStatus.Running;
// 多次异常后需要重新计算优先级
if (job.TryCount <= (job.MaxTryCount / 2) &&
job.TryCount > (job.MaxTryCount / 3))
// 一次性任务执行一次后标记为已完成
if (job.JobType == JobType.Once)
{
job.Priority = JobPriority.BelowNormal;
}
else if (job.TryCount > (job.MaxTryCount / 1.5))
{
job.Priority = JobPriority.Low;
job.Status = JobStatus.Completed;
}
if (job.TryCount >= job.MaxTryCount)
// 任务异常后可重试
if (context.EventData.Exception != null)
{
job.Status = JobStatus.Stopped;
job.IsAbandoned = true;
job.NextRunTime = null;
await RemoveJobAsync(context, job);
// 重试达到上限发布异常通知
await NotifierAsync(context, job);
job.TryCount += 1;
job.IsAbandoned = false;
// 将任务标记为运行中, 会被轮询重新进入队列
job.Status = JobStatus.FailedRetry;
job.Result = GetExceptionMessage(context.EventData.Exception);
// 多次异常后需要重新计算优先级
if (job.TryCount <= (job.MaxTryCount / 2) &&
job.TryCount > (job.MaxTryCount / 3))
{
job.Priority = JobPriority.BelowNormal;
}
else if (job.TryCount > (job.MaxTryCount / 1.5))
{
job.Priority = JobPriority.Low;
}
if (job.TryCount >= job.MaxTryCount)
{
job.Status = JobStatus.Stopped;
job.IsAbandoned = true;
job.NextRunTime = null;
await RemoveJobAsync(context, job);
// 重试达到上限发布异常通知
await NotifierAsync(context, job);
}
}
}
else
{
// 所有任务达到上限则标记已完成
if (job.MaxCount > 0 && job.TriggerCount >= job.MaxCount)
else
{
job.Status = JobStatus.Completed;
job.NextRunTime = null;
// 所有任务达到上限则标记已完成
if (job.MaxCount > 0 && job.TriggerCount >= job.MaxCount)
{
job.Status = JobStatus.Completed;
job.NextRunTime = null;
await RemoveJobAsync(context, job);
await RemoveJobAsync(context, job);
}
}
}
await store.StoreAsync(job);
await store.StoreAsync(job);
}
}
}

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

@ -2,7 +2,7 @@
using System.Threading.Tasks;
using Volo.Abp.Auditing;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Uow;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.BackgroundTasks.Internal;
@ -21,7 +21,11 @@ public class JobLogEvent : JobEventBase<JobLogEvent>, ITransientDependency
return;
}
var store = context.ServiceProvider.GetRequiredService<IJobStore>();
var currentTenant = context.ServiceProvider.GetRequiredService<ICurrentTenant>();
await store.StoreLogAsync(context.EventData);
using (currentTenant.Change(context.EventData.TenantId))
{
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 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 Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.TaskManagement.Permissions;
@ -11,50 +10,42 @@ public class TaskManagementPermissionDefinitionProvider : PermissionDefinitionPr
{
var group = context.AddGroup(
TaskManagementPermissions.GroupName,
L("Permissions:TaskManagement"),
MultiTenancySides.Host);
L("Permissions:TaskManagement"));
var backgroundJobs = group.AddPermission(
TaskManagementPermissions.BackgroundJobs.Default,
L("Permissions:BackgroundJobs"),
MultiTenancySides.Host);
L("Permissions:BackgroundJobs"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Create,
L("Permissions:CreateJob"),
MultiTenancySides.Host);
L("Permissions:CreateJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Update,
L("Permissions:UpdateJob"),
MultiTenancySides.Host);
L("Permissions:UpdateJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Delete,
L("Permissions:DeleteJob"),
MultiTenancySides.Host);
L("Permissions:DeleteJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Trigger,
L("Permissions:TriggerJob"),
MultiTenancySides.Host);
L("Permissions:TriggerJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Pause,
L("Permissions:PauseJob"),
MultiTenancySides.Host);
L("Permissions:PauseJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Resume,
L("Permissions:ResumeJob"),
MultiTenancySides.Host);
L("Permissions:ResumeJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Start,
L("Permissions:StartJob"));
backgroundJobs.AddChild(
TaskManagementPermissions.BackgroundJobs.Stop,
L("Permissions:StopJob"),
MultiTenancySides.Host);
L("Permissions:StopJob"));
var backgroundJobLogs = group.AddPermission(
TaskManagementPermissions.BackgroundJobLogs.Default,
L("Permissions:BackgroundJobLogs"),
MultiTenancySides.Host);
L("Permissions:BackgroundJobLogs"));
backgroundJobLogs.AddChild(
TaskManagementPermissions.BackgroundJobLogs.Delete,
L("Permissions:DeleteJobLogs"),
MultiTenancySides.Host);
L("Permissions:DeleteJobLogs"));
}
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 Pause = Default + ".Pause";
public const string Resume = Default + ".Resume";
public const string Start = Default + ".Start";
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 System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
@ -44,7 +45,8 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
input.EndTime,
input.Priority,
input.MaxCount,
input.MaxTryCount);
input.MaxTryCount,
CurrentTenant.Id);
UpdateByInput(backgroundJobInfo, input);
@ -132,6 +134,14 @@ public class BackgroundJobInfoAppService : TaskManagementApplicationService, IBa
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)]
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);
}
[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)
{
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:PauseJob": "Pause Job",
"Permissions:ResumeJob": "Resume Job",
"Permissions:StartJob": "Start Job",
"Permissions:StopJob": "Stop Job",
"Permissions:BackgroundJobLogs": "BackgroundJobs Logs",
"Permissions:DeleteJobLogs": "Delete Job Logs",
@ -72,8 +73,11 @@
"BackgroundJobs:Pause": "Pause",
"BackgroundJobs:Resume": "Resume",
"BackgroundJobs:Trigger": "Trigger",
"BackgroundJobs:Stop": "Stop",
"BackgroundJobs:Start": "Start Jobs",
"BackgroundJobs:Stop": "Stop Jobs",
"BackgroundJobs:Delete": "Delete",
"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:PauseJob": "暂停作业",
"Permissions:ResumeJob": "恢复作业",
"Permissions:StartJob": "启动作业",
"Permissions:StopJob": "停止作业",
"Permissions:BackgroundJobLogs": "日志管理",
"Permissions:DeleteJobLogs": "删除作业日志",
@ -72,8 +73,11 @@
"BackgroundJobs:Pause": "暂停",
"BackgroundJobs:Resume": "恢复",
"BackgroundJobs:Trigger": "触发",
"BackgroundJobs:Stop": "停止",
"BackgroundJobs:Start": "启动作业",
"BackgroundJobs:Stop": "停止作业",
"BackgroundJobs:Delete": "删除",
"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.Data;
using Volo.Abp.Domain.Entities.Auditing;
using Volo.Abp.MultiTenancy;
namespace LINGYUN.Abp.TaskManagement;
public class BackgroundJobInfo : AuditedAggregateRoot<Guid>
public class BackgroundJobInfo : AuditedAggregateRoot<Guid>, IMultiTenant
{
public virtual Guid? TenantId { get; protected set; }
/// <summary>
/// 任务名称
/// </summary>
@ -114,7 +116,8 @@ public class BackgroundJobInfo : AuditedAggregateRoot<Guid>
DateTime? endTime = null,
JobPriority priority = JobPriority.Normal,
int maxCount = 0,
int maxTryCount = 50) : base(id)
int maxTryCount = 50,
Guid? tenantId = null) : base(id)
{
Name = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength);
Group = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength);
@ -125,6 +128,7 @@ public class BackgroundJobInfo : AuditedAggregateRoot<Guid>
MaxCount = maxCount;
MaxTryCount = maxTryCount;
TenantId = tenantId;
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 Volo.Abp;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
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 string JobName { get; protected set; }
public virtual string JobGroup { get; protected set; }
@ -18,12 +20,14 @@ public class BackgroundJobLog : Entity<long>
string type,
string group,
string name,
DateTime runTime)
DateTime runTime,
Guid? tenantId = null)
{
JobType = Check.NotNullOrWhiteSpace(type, nameof(type), BackgroundJobInfoConsts.MaxTypeLength);
JobGroup = Check.NotNullOrWhiteSpace(group, nameof(group), BackgroundJobInfoConsts.MaxGroupLength);
JobName = Check.NotNullOrWhiteSpace(name, nameof(name), BackgroundJobInfoConsts.MaxNameLength);
RunTime = runTime;
TenantId = tenantId;
}
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 System.Threading.Tasks;
using System.Collections.Generic;
using Volo.Abp;
using Volo.Abp.Domain.Services;
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)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
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)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -99,6 +114,14 @@ public class BackgroundJobManager : DomainService
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)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -110,6 +133,14 @@ public class BackgroundJobManager : DomainService
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)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -122,6 +153,14 @@ public class BackgroundJobManager : DomainService
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)
{
var job = ObjectMapper.Map<BackgroundJobInfo, JobInfo>(jobInfo);
@ -132,4 +171,12 @@ public class BackgroundJobManager : DomainService
await BackgroundJobInfoRepository.UpdateAsync(jobInfo);
}
public virtual async Task BulkStopAsync(IEnumerable<BackgroundJobInfo> jobInfos)
{
foreach (var jobInfo in jobInfos)
{
await StopAsync(jobInfo);
}
}
}

155
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.Threading;
using System.Threading.Tasks;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.MultiTenancy;
using Volo.Abp.ObjectMapping;
using Volo.Abp.Uow;
@ -12,32 +14,44 @@ namespace LINGYUN.Abp.TaskManagement;
[Dependency(ReplaceServices = true)]
public class BackgroundJobStore : IJobStore, ITransientDependency
{
protected IDataFilter DataFilter { get; }
protected IObjectMapper ObjectMapper { get; }
protected ICurrentTenant CurrentTenant { get; }
protected IBackgroundJobInfoRepository JobInfoRepository { get; }
protected IBackgroundJobLogRepository JobLogRepository { get; }
public BackgroundJobStore(
IDataFilter dataFilter,
IObjectMapper objectMapper,
ICurrentTenant currentTenant,
IBackgroundJobInfoRepository jobInfoRepository,
IBackgroundJobLogRepository jobLogRepository)
{
DataFilter = dataFilter;
ObjectMapper = objectMapper;
CurrentTenant = currentTenant;
JobInfoRepository = jobInfoRepository;
JobLogRepository = jobLogRepository;
}
public async virtual Task<List<JobInfo>> GetAllPeriodTasksAsync(CancellationToken cancellationToken = default)
{
var jobInfos = await JobInfoRepository.GetAllPeriodTasksAsync(cancellationToken);
using (DataFilter.Disable<IMultiTenant>())
{
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)
{
var jobInfos = await JobInfoRepository.GetWaitingListAsync(maxResultCount, cancellationToken);
using (DataFilter.Disable<IMultiTenant>())
{
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)
@ -50,78 +64,84 @@ public class BackgroundJobStore : IJobStore, ITransientDependency
[UnitOfWork]
public async virtual Task StoreAsync(JobInfo jobInfo)
{
var backgroundJobInfo = await JobInfoRepository.FindAsync(jobInfo.Id);
if (backgroundJobInfo != null)
using (CurrentTenant.Change(jobInfo.TenantId))
{
backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime);
backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime);
backgroundJobInfo.SetStatus(jobInfo.Status);
backgroundJobInfo.SetResult(jobInfo.Result);
backgroundJobInfo.TriggerCount = jobInfo.TriggerCount;
backgroundJobInfo.TryCount = jobInfo.TryCount;
backgroundJobInfo.IsAbandoned = jobInfo.IsAbandoned;
await JobInfoRepository.UpdateAsync(backgroundJobInfo);
}
else
{
backgroundJobInfo = new BackgroundJobInfo(
jobInfo.Id,
jobInfo.Name,
jobInfo.Group,
jobInfo.Type,
jobInfo.Args,
jobInfo.BeginTime,
jobInfo.EndTime,
jobInfo.Priority,
jobInfo.MaxCount,
jobInfo.MaxTryCount)
var backgroundJobInfo = await JobInfoRepository.FindAsync(jobInfo.Id);
if (backgroundJobInfo != null)
{
IsEnabled = true,
TriggerCount = jobInfo.TriggerCount,
IsAbandoned = jobInfo.IsAbandoned,
TryCount = jobInfo.TryCount,
LockTimeOut = jobInfo.LockTimeOut,
Description = jobInfo.Description
};
backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime);
backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime);
backgroundJobInfo.SetStatus(jobInfo.Status);
backgroundJobInfo.SetResult(jobInfo.Result);
switch (jobInfo.JobType)
backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime);
backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime);
backgroundJobInfo.SetStatus(jobInfo.Status);
backgroundJobInfo.SetResult(jobInfo.Result);
backgroundJobInfo.TriggerCount = jobInfo.TriggerCount;
backgroundJobInfo.TryCount = jobInfo.TryCount;
backgroundJobInfo.IsAbandoned = jobInfo.IsAbandoned;
await JobInfoRepository.UpdateAsync(backgroundJobInfo);
}
else
{
case JobType.Once:
backgroundJobInfo.SetOnceJob(jobInfo.Interval);
break;
case JobType.Persistent:
backgroundJobInfo.SetPersistentJob(jobInfo.Interval);
break;
case JobType.Period:
backgroundJobInfo.SetPeriodJob(jobInfo.Cron);
break;
backgroundJobInfo = new BackgroundJobInfo(
jobInfo.Id,
jobInfo.Name,
jobInfo.Group,
jobInfo.Type,
jobInfo.Args,
jobInfo.BeginTime,
jobInfo.EndTime,
jobInfo.Priority,
jobInfo.MaxCount,
jobInfo.MaxTryCount)
{
IsEnabled = true,
TriggerCount = jobInfo.TriggerCount,
IsAbandoned = jobInfo.IsAbandoned,
TryCount = jobInfo.TryCount,
LockTimeOut = jobInfo.LockTimeOut,
Description = jobInfo.Description
};
backgroundJobInfo.SetNextRunTime(jobInfo.NextRunTime);
backgroundJobInfo.SetLastRunTime(jobInfo.LastRunTime);
backgroundJobInfo.SetStatus(jobInfo.Status);
backgroundJobInfo.SetResult(jobInfo.Result);
switch (jobInfo.JobType)
{
case JobType.Once:
backgroundJobInfo.SetOnceJob(jobInfo.Interval);
break;
case JobType.Persistent:
backgroundJobInfo.SetPersistentJob(jobInfo.Interval);
break;
case JobType.Period:
backgroundJobInfo.SetPeriodJob(jobInfo.Cron);
break;
}
await JobInfoRepository.InsertAsync(backgroundJobInfo);
}
await JobInfoRepository.InsertAsync(backgroundJobInfo);
}
}
[UnitOfWork]
public async virtual Task StoreLogAsync(JobEventData eventData)
{
var jogLog = new BackgroundJobLog(
eventData.Type.Name,
using (CurrentTenant.Change(eventData.TenantId))
{
var jogLog = new BackgroundJobLog(
eventData.Type.Name,
eventData.Group,
eventData.Name,
eventData.RunTime)
{
JobId = eventData.Key
};
{
JobId = eventData.Key
};
jogLog.SetMessage(
eventData.Exception == null ? eventData.Result ?? "OK" : "Failed",
eventData.Exception);
jogLog.SetMessage(
eventData.Exception == null ? eventData.Result ?? "OK" : "Failed",
eventData.Exception);
await JobLogRepository.InsertAsync(jogLog);
await JobLogRepository.InsertAsync(jogLog);
}
}
[UnitOfWork]
@ -130,11 +150,14 @@ public class BackgroundJobStore : IJobStore, ITransientDependency
TimeSpan jobExpiratime,
CancellationToken cancellationToken = default)
{
var jobs = await JobInfoRepository.GetExpiredJobsAsync(
maxResultCount,
jobExpiratime,
cancellationToken);
using (DataFilter.Disable<IMultiTenant>())
{
var jobs = await JobInfoRepository.GetExpiredJobsAsync(
maxResultCount,
jobExpiratime,
cancellationToken);
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.BeginCreationTime.HasValue, x => x.CreationTime.CompareTo(filter.BeginCreationTime.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)
.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);
}
[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")
.HasColumnType("int");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.Property<int>("TriggerCount")
.HasColumnType("int");
@ -179,6 +183,10 @@ namespace LY.MicroService.TaskManagement.Migrations
b.Property<DateTime>("RunTime")
.HasColumnType("datetime(6)");
b.Property<Guid?>("TenantId")
.HasColumnType("char(36)")
.HasColumnName("TenantId");
b.HasKey("Id");
b.HasIndex("JobGroup", "JobName");

Loading…
Cancel
Save