14 changed files with 375 additions and 0 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,21 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFrameworks>net8.0;net9.0;net10.0</TargetFrameworks> |
|||
<AssemblyName>LINGYUN.Abp.WorkdayCalendar</AssemblyName> |
|||
<PackageId>LINGYUN.Abp.WorkdayCalendar</PackageId> |
|||
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute> |
|||
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute> |
|||
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute> |
|||
<Nullable>enable</Nullable> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Timing" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,9 @@ |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.Timing; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
[DependsOn(typeof(AbpTimingModule))] |
|||
public class AbpCalendarModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,40 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
public class AbpCalendarOptions |
|||
{ |
|||
/// <summary>
|
|||
/// 默认工作日范围
|
|||
/// 默认: 周一到周五
|
|||
/// </summary>
|
|||
public DayOfWeek[] DefaultWorkdays { get; set; } |
|||
/// <summary>
|
|||
/// 是否启用工作时间限制
|
|||
/// 默认: true
|
|||
/// </summary>
|
|||
public bool EnableWorkTimeRestriction { get; set; } |
|||
/// <summary>
|
|||
/// 默认工作时间范围
|
|||
/// 默认: 09:00-12:00、13:00-18:00
|
|||
/// </summary>
|
|||
public WorkTimeRange[] DefaultWorkTimes { get; set; } |
|||
|
|||
public AbpCalendarOptions() |
|||
{ |
|||
DefaultWorkdays = new[] |
|||
{ |
|||
DayOfWeek.Monday, |
|||
DayOfWeek.Tuesday, |
|||
DayOfWeek.Wednesday, |
|||
DayOfWeek.Thursday, |
|||
DayOfWeek.Friday |
|||
}; |
|||
EnableWorkTimeRestriction = true; |
|||
DefaultWorkTimes = new[] |
|||
{ |
|||
new WorkTimeRange(new TimeSpan(9, 0, 0), new TimeSpan(12, 0, 0)), |
|||
new WorkTimeRange(new TimeSpan(13, 0, 0), new TimeSpan(18, 0, 0)), |
|||
}; |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
public interface IWorkdayCalendarService |
|||
{ |
|||
/// <summary>
|
|||
/// 检查指定时间是否在工作日
|
|||
/// </summary>
|
|||
Task<bool> IsWorkdayAsync(DateOnly date); |
|||
/// <summary>
|
|||
/// 检查指定时间是否在工作时间
|
|||
/// </summary>
|
|||
/// <param name="dateTime"></param>
|
|||
/// <returns></returns>
|
|||
Task<bool> IsInWorkTimeAsync(DateTime dateTime); |
|||
/// <summary>
|
|||
/// 获取下一个工作日
|
|||
/// </summary>
|
|||
/// <param name="date"></param>
|
|||
/// <returns></returns>
|
|||
Task<DateOnly> GetNextWorkdayAsync(DateOnly date); |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
public interface IWorkdayCalendarStore |
|||
{ |
|||
/// <summary>
|
|||
/// 获取特殊日期
|
|||
/// </summary>
|
|||
/// <param name="date">日期</param>
|
|||
/// <returns></returns>
|
|||
Task<SpecialDateInfo?> FindSpecialDateAsync(DateOnly date); |
|||
/// <summary>
|
|||
/// 获取默认工作日历
|
|||
/// </summary>
|
|||
Task<WorkdayCalendarInfo?> FindDefaultCalendarAsync(); |
|||
} |
|||
@ -0,0 +1,19 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
[Dependency(TryRegister = true)] |
|||
public class NullWorkdayCalendarStore : IWorkdayCalendarStore, ISingletonDependency |
|||
{ |
|||
public Task<WorkdayCalendarInfo?> FindDefaultCalendarAsync() |
|||
{ |
|||
return Task.FromResult<WorkdayCalendarInfo?>(null); |
|||
} |
|||
|
|||
public Task<SpecialDateInfo?> FindSpecialDateAsync(DateOnly date) |
|||
{ |
|||
return Task.FromResult<SpecialDateInfo?>(null); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
/// <summary>
|
|||
/// 特殊日期
|
|||
/// </summary>
|
|||
public class SpecialDateInfo |
|||
{ |
|||
/// <summary>
|
|||
/// 日期
|
|||
/// </summary>
|
|||
public DateOnly Date { get; set; } |
|||
/// <summary>
|
|||
/// 特殊日期类型
|
|||
/// </summary>
|
|||
public SpecialDateType Type { get; set; } |
|||
/// <summary>
|
|||
/// 特殊日期描述
|
|||
/// </summary>
|
|||
public string? Description { get; set; } |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
using System.ComponentModel; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
/// <summary>
|
|||
/// 特殊日期类型
|
|||
/// </summary>
|
|||
public enum SpecialDateType |
|||
{ |
|||
/// <summary>
|
|||
/// 法定节假日
|
|||
/// </summary>
|
|||
[Description("法定节假日")] |
|||
Holiday = 0, |
|||
/// <summary>
|
|||
/// 特殊工作日
|
|||
/// </summary>
|
|||
[Description("特殊工作日")] |
|||
SpecialWorkday = 1, |
|||
/// <summary>
|
|||
/// 自定义假期
|
|||
/// </summary>
|
|||
[Description("自定义假期")] |
|||
CustomHoliday = 2 |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
/// <summary>
|
|||
/// 工作时间范围
|
|||
/// </summary>
|
|||
public class WorkTimeRange |
|||
{ |
|||
/// <summary>
|
|||
/// 开始时间
|
|||
/// </summary>
|
|||
public TimeSpan StartTime { get; set; } |
|||
/// <summary>
|
|||
/// 截止时间
|
|||
/// </summary>
|
|||
public TimeSpan EndTime { get; set; } |
|||
public WorkTimeRange() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public WorkTimeRange(TimeSpan startTime, TimeSpan endTime) |
|||
{ |
|||
StartTime = startTime; |
|||
EndTime = endTime; |
|||
} |
|||
} |
|||
@ -0,0 +1,34 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
/// <summary>
|
|||
/// 工作日历信息
|
|||
/// </summary>
|
|||
public class WorkdayCalendarInfo |
|||
{ |
|||
/// <summary>
|
|||
/// 日历名称
|
|||
/// </summary>
|
|||
public string Name { get; set; } |
|||
/// <summary>
|
|||
/// 默认日历
|
|||
/// </summary>
|
|||
public bool IsDefault { get; set; } |
|||
/// <summary>
|
|||
/// 工作日范围
|
|||
/// </summary>
|
|||
public DayOfWeek[] Workdays { get; set; } |
|||
/// <summary>
|
|||
/// 工作时间范围
|
|||
/// </summary>
|
|||
public WorkTimeRange[]? WorkTimes { get; set; } |
|||
/// <summary>
|
|||
/// 是否启用工作时间限制
|
|||
/// </summary>
|
|||
public bool EnableWorkTimeRestriction { get; set; } |
|||
/// <summary>
|
|||
/// 特殊日期列表
|
|||
/// </summary>
|
|||
public SpecialDateInfo[]? SpecialDates { get; set; } |
|||
} |
|||
@ -0,0 +1,117 @@ |
|||
using Microsoft.Extensions.Options; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.DependencyInjection; |
|||
|
|||
namespace LINGYUN.Abp.Calendar; |
|||
|
|||
public class WorkdayCalendarService : IWorkdayCalendarService, ITransientDependency |
|||
{ |
|||
private readonly AbpCalendarOptions _options; |
|||
private readonly IWorkdayCalendarStore _workdayCalendarStore; |
|||
|
|||
public WorkdayCalendarService( |
|||
IOptions<AbpCalendarOptions> options, |
|||
IWorkdayCalendarStore workdayCalendarStore) |
|||
{ |
|||
_options = options.Value; |
|||
_workdayCalendarStore = workdayCalendarStore; |
|||
} |
|||
|
|||
public async virtual Task<DateOnly> GetNextWorkdayAsync(DateOnly date) |
|||
{ |
|||
var nextDay = date.AddDays(1); |
|||
// 以31天为限
|
|||
var maxAttempts = 30; |
|||
|
|||
for (var i = 0; i < maxAttempts; i++) |
|||
{ |
|||
if (await IsWorkdayAsync(nextDay)) |
|||
{ |
|||
return nextDay; |
|||
} |
|||
nextDay = nextDay.AddDays(1); |
|||
} |
|||
|
|||
// 如果30天内都找不到工作日,返回原日期+1天
|
|||
return date.AddDays(1); |
|||
} |
|||
|
|||
public async virtual Task<bool> IsWorkdayAsync(DateOnly date) |
|||
{ |
|||
// 特殊工作日定义
|
|||
var specialDate = await _workdayCalendarStore.FindSpecialDateAsync(date); |
|||
if (specialDate != null) |
|||
{ |
|||
return specialDate.Type == SpecialDateType.SpecialWorkday; |
|||
} |
|||
// 获取默认工作日历定义
|
|||
var calendar = await _workdayCalendarStore.FindDefaultCalendarAsync(); |
|||
if (calendar == null) |
|||
{ |
|||
// 未定义默认工作日历, 获取系统定义工作日范围
|
|||
return _options.DefaultWorkdays.Contains(date.DayOfWeek); |
|||
} |
|||
// 特殊工作日范围
|
|||
if (calendar.SpecialDates != null && |
|||
calendar.SpecialDates.Any(x => x.Date == date && x.Type == SpecialDateType.SpecialWorkday)) |
|||
{ |
|||
return true; |
|||
} |
|||
// 工作日范围
|
|||
if (!calendar.Workdays.Contains(date.DayOfWeek)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
|
|||
public async virtual Task<bool> IsInWorkTimeAsync(DateTime time) |
|||
{ |
|||
var nowDate = DateOnly.FromDateTime(time); |
|||
// 特殊工作日定义
|
|||
var specialDate = await _workdayCalendarStore.FindSpecialDateAsync(nowDate); |
|||
if (specialDate != null) |
|||
{ |
|||
return specialDate.Type == SpecialDateType.SpecialWorkday; |
|||
} |
|||
// 获取默认工作日历定义
|
|||
var calendar = await _workdayCalendarStore.FindDefaultCalendarAsync(); |
|||
if (calendar == null) |
|||
{ |
|||
// 未定义默认工作日历, 获取系统定义工作日范围
|
|||
if (!_options.DefaultWorkdays.Contains(nowDate.DayOfWeek)) |
|||
{ |
|||
return false; |
|||
} |
|||
if (_options.EnableWorkTimeRestriction && |
|||
!_options.DefaultWorkTimes.Any(timeRange => timeRange.StartTime >= time.TimeOfDay && timeRange.EndTime <= time.TimeOfDay)) |
|||
{ |
|||
return false; |
|||
} |
|||
|
|||
return true; |
|||
} |
|||
// 特殊工作日范围
|
|||
if (calendar.SpecialDates != null && |
|||
calendar.SpecialDates.Any(x => x.Date == nowDate && x.Type == SpecialDateType.SpecialWorkday)) |
|||
{ |
|||
return true; |
|||
} |
|||
// 工作日范围
|
|||
if (!calendar.Workdays.Contains(nowDate.DayOfWeek)) |
|||
{ |
|||
return false; |
|||
} |
|||
// 工作时间范围
|
|||
if (calendar.EnableWorkTimeRestriction && |
|||
calendar.WorkTimes != null) |
|||
{ |
|||
return calendar.WorkTimes.Any(x => x.StartTime >= time.TimeOfDay && x.EndTime <= time.TimeOfDay); |
|||
} |
|||
|
|||
return false; |
|||
} |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
# LINGYUN.Abp.Calendar |
|||
|
|||
日历模块, 定义系统日历参数, 用于特定场景. |
|||
|
|||
#### 注意 |
|||
|
|||
|
|||
## 功能特性 |
|||
|
|||
|
|||
## 配置使用 |
|||
|
|||
|
|||
## 更多信息 |
|||
|
|||
Loading…
Reference in new issue