mirror of
				https://github.com/nsnail/NetAdmin.git
				synced 2025-11-04 05:05:27 +08:00 
			
		
		
		
	@@ -8,21 +8,13 @@ namespace NetAdmin.Application.Services;
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <typeparam name="TEntity">实体类型</typeparam>
 | 
			
		||||
/// <typeparam name="TLogger">日志类型</typeparam>
 | 
			
		||||
public abstract class RepositoryService<TEntity, TLogger> : ServiceBase<TLogger>
 | 
			
		||||
public abstract class RepositoryService<TEntity, TLogger>(DefaultRepository<TEntity> rpo) : ServiceBase<TLogger>
 | 
			
		||||
    where TEntity : EntityBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="RepositoryService{TEntity, TLogger}" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected RepositoryService(DefaultRepository<TEntity> rpo) //
 | 
			
		||||
    {
 | 
			
		||||
        Rpo = rpo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     默认仓储
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected DefaultRepository<TEntity> Rpo { get; }
 | 
			
		||||
    protected DefaultRepository<TEntity> Rpo => rpo;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     启用级联保存
 | 
			
		||||
 
 | 
			
		||||
@@ -5,21 +5,13 @@ namespace NetAdmin.Cache;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     缓存基类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class CacheBase<TCacheContainer, TService> : ICache<TCacheContainer, TService>
 | 
			
		||||
public abstract class CacheBase<TCacheContainer, TService>(TCacheContainer cache, TService service)
 | 
			
		||||
    : ICache<TCacheContainer, TService>
 | 
			
		||||
    where TService : IService
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="CacheBase{TCacheLoad, TService}" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected CacheBase(TCacheContainer cache, TService service)
 | 
			
		||||
    {
 | 
			
		||||
        Cache   = cache;
 | 
			
		||||
        Service = service;
 | 
			
		||||
    }
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public TCacheContainer Cache => cache;
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public TCacheContainer Cache { get; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public TService Service { get; }
 | 
			
		||||
    public TService Service => service;
 | 
			
		||||
}
 | 
			
		||||
@@ -6,15 +6,10 @@ namespace NetAdmin.Cache;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     分布式缓存
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class DistributedCache<TService> : CacheBase<IDistributedCache, TService>
 | 
			
		||||
public abstract class DistributedCache<TService>(IDistributedCache cache, TService service)
 | 
			
		||||
    : CacheBase<IDistributedCache, TService>(cache, service)
 | 
			
		||||
    where TService : IService
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="DistributedCache{TService}" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected DistributedCache(IDistributedCache cache, TService service) //
 | 
			
		||||
        : base(cache, service) { }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     创建缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -5,12 +5,6 @@ namespace NetAdmin.Cache;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     内存缓存
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class MemoryCache<TService> : CacheBase<IMemoryCache, TService>
 | 
			
		||||
    where TService : IService
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="MemoryCache{TService}" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected MemoryCache(IMemoryCache cache, TService service) //
 | 
			
		||||
        : base(cache, service) { }
 | 
			
		||||
}
 | 
			
		||||
public abstract class MemoryCache<TService>(IMemoryCache cache, TService service)
 | 
			
		||||
    : CacheBase<IMemoryCache, TService>(cache, service)
 | 
			
		||||
    where TService : IService;
 | 
			
		||||
@@ -8,11 +8,14 @@ public abstract record DataAbstraction
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     如果数据校验失败,抛出异常
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <exception cref="NetAdminInvalidInputException">NetAdminInvalidInputException</exception>
 | 
			
		||||
    /// <exception cref="NetAdminValidateException">NetAdminValidateException</exception>
 | 
			
		||||
    public void ThrowIfInvalid()
 | 
			
		||||
    {
 | 
			
		||||
        if (!this.TryValidate().IsValid) {
 | 
			
		||||
            throw new NetAdminInvalidInputException(Ln.无效输入);
 | 
			
		||||
        var validationResult = this.TryValidate();
 | 
			
		||||
        if (!validationResult.IsValid) {
 | 
			
		||||
            throw new NetAdminValidateException(validationResult.ValidationResults.ToDictionary( //
 | 
			
		||||
                                                    x => x.MemberNames.First()                   //
 | 
			
		||||
                                                  , x => new[] { x.ErrorMessage }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,12 @@ public abstract record VersionEntity<T> : LiteVersionEntity<T>, IFieldModifiedUs
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    [Column(CanUpdate = false, Position = -1)]
 | 
			
		||||
    public long? CreatedUserId { get; init; }
 | 
			
		||||
    public virtual long? CreatedUserId { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanUpdate = false, Position = -1)]
 | 
			
		||||
    public string CreatedUserName { get; init; }
 | 
			
		||||
    public virtual string CreatedUserName { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldPrimary{T}.Id" />
 | 
			
		||||
    [Column(IsIdentity = false, IsPrimary = true, Position = 1)]
 | 
			
		||||
@@ -34,10 +34,10 @@ public abstract record VersionEntity<T> : LiteVersionEntity<T>, IFieldModifiedUs
 | 
			
		||||
    /// <inheritdoc cref="IFieldModifiedUser.ModifiedUserId" />
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    [Column(CanInsert = false, Position = -1)]
 | 
			
		||||
    public long? ModifiedUserId { get; init; }
 | 
			
		||||
    public virtual long? ModifiedUserId { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldModifiedUser.ModifiedUserName" />
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31, CanInsert = false, Position = -1)]
 | 
			
		||||
    public string ModifiedUserName { get; init; }
 | 
			
		||||
    public virtual string ModifiedUserName { get; init; }
 | 
			
		||||
}
 | 
			
		||||
@@ -98,6 +98,13 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
 | 
			
		||||
    public virtual string Summary { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     执行用户
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Navigate(nameof(UserId))]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public Sys_User User { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     执行用户编号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,68 +15,68 @@ public record Sys_JobRecord : LiteImmutableEntity
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public long Duration { get; init; }
 | 
			
		||||
    public virtual long Duration { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     请求方法
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    [Column]
 | 
			
		||||
    public HttpMethods HttpMethod { get; init; }
 | 
			
		||||
    public virtual HttpMethods HttpMethod { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     HTTP 状态码
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public HttpStatusCode HttpStatusCode { get; init; }
 | 
			
		||||
    public virtual int HttpStatusCode { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     作业编号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public long JobId { get; init; }
 | 
			
		||||
    public virtual long JobId { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     请求体
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public string RequestBody { get; init; }
 | 
			
		||||
    public virtual string RequestBody { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     请求头
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public string RequestHeader { get; init; }
 | 
			
		||||
    public virtual string RequestHeader { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     请求的网络地址
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public string RequestUrl { get; init; }
 | 
			
		||||
    public virtual string RequestUrl { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     响应体
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public string ResponseBody { get; init; }
 | 
			
		||||
    public virtual string ResponseBody { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     响应头
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public string ResponseHeader { get; init; }
 | 
			
		||||
    public virtual string ResponseHeader { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     执行时间编号
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public long TimeId { get; init; }
 | 
			
		||||
    public virtual long TimeId { get; init; }
 | 
			
		||||
}
 | 
			
		||||
@@ -28,7 +28,7 @@ public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    [Column(Position = -1)]
 | 
			
		||||
    [JsonIgnore]
 | 
			
		||||
    public virtual int? CreatedClientIp { get; init; }
 | 
			
		||||
    public int? CreatedClientIp { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     创建者来源地址
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
///     角色-接口映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleApi))]
 | 
			
		||||
public sealed record Sys_RoleApi : ImmutableEntity
 | 
			
		||||
public record Sys_RoleApi : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     关联的接口
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_RoleDept))]
 | 
			
		||||
[Index($"idx_{{tablename}}_{nameof(RoleId)}_{nameof(DeptId)}", $"{nameof(RoleId)},{nameof(DeptId)}", true)]
 | 
			
		||||
public sealed record Sys_RoleDept : ImmutableEntity
 | 
			
		||||
public record Sys_RoleDept : ImmutableEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     关联的部门
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
///     用户-角色映射表
 | 
			
		||||
/// </summary>
 | 
			
		||||
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_UserRole))]
 | 
			
		||||
public sealed record Sys_UserRole : VersionEntity
 | 
			
		||||
public record Sys_UserRole : VersionEntity
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     关联的角色
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.Dto.Dependency;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     动态过滤条件
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record DynamicFilterInfo : DataAbstraction
 | 
			
		||||
public sealed record DynamicFilterInfo : DataAbstraction
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     字段名
 | 
			
		||||
 
 | 
			
		||||
@@ -6,10 +6,10 @@ namespace NetAdmin.Domain.Dto.Sys.Dept;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     响应:查询部门
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record QueryDeptRsp : Sys_Dept
 | 
			
		||||
public sealed record QueryDeptRsp : Sys_Dept
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Sys_Dept.Children" />
 | 
			
		||||
    public new virtual IEnumerable<QueryDeptRsp> Children { get; init; }
 | 
			
		||||
    public new IEnumerable<QueryDeptRsp> Children { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using NetAdmin.Domain.DbMaps.Dependency.Fields;
 | 
			
		||||
using NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
using NetAdmin.Domain.Enums.Sys;
 | 
			
		||||
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
 | 
			
		||||
 | 
			
		||||
@@ -14,6 +15,14 @@ public sealed record QueryJobRsp : Sys_Job
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override DateTime CreatedTime { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldCreatedUser.CreatedUserId" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override long? CreatedUserId { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldCreatedUser.CreatedUserName" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string CreatedUserName { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldEnabled.Enabled" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override bool Enabled { get; init; }
 | 
			
		||||
@@ -42,6 +51,18 @@ public sealed record QueryJobRsp : Sys_Job
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override HttpStatusCode? LastStatusCode { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldModifiedTime.ModifiedTime" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override DateTime? ModifiedTime { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldModifiedUser.ModifiedUserId" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override long? ModifiedUserId { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldModifiedUser.ModifiedUserName" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string ModifiedUserName { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_Job.NextExecTime" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override DateTime? NextExecTime { get; init; }
 | 
			
		||||
@@ -70,6 +91,9 @@ public sealed record QueryJobRsp : Sys_Job
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string Summary { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_Job.User" />
 | 
			
		||||
    public new QueryUserRsp User { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_Job.UserId" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override long UserId { get; init; }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using NetAdmin.Domain.DbMaps.Dependency.Fields;
 | 
			
		||||
using NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
 | 
			
		||||
 | 
			
		||||
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
 | 
			
		||||
 | 
			
		||||
@@ -8,7 +9,51 @@ namespace NetAdmin.Domain.Dto.Sys.JobRecord;
 | 
			
		||||
/// </summary>
 | 
			
		||||
public sealed record QueryJobRecordRsp : Sys_JobRecord
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override DateTime CreatedTime { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.Duration" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override long Duration { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.HttpMethod" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override HttpMethods HttpMethod { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.HttpStatusCode" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override int HttpStatusCode { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="IFieldPrimary{T}.Id" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override long Id { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.JobId" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override long JobId { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.RequestBody" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string RequestBody { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.RequestHeader" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string RequestHeader { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.RequestUrl" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string RequestUrl { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.ResponseBody" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string ResponseBody { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.ResponseHeader" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
 | 
			
		||||
    public override string ResponseHeader { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc cref="Sys_JobRecord.TimeId" />
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public override long TimeId { get; init; }
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,7 @@ namespace NetAdmin.Domain.Dto.Sys.Tool;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     响应:获取模块信息
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record GetModulesRsp : DataAbstraction
 | 
			
		||||
public sealed record GetModulesRsp : DataAbstraction
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     模块名称
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@ namespace NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     请求:创建用户
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record CreateUserReq : CreateUpdateUserReq, IRegister
 | 
			
		||||
public sealed record CreateUserReq : CreateUpdateUserReq, IRegister
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="CreateUpdateUserReq.PasswordText" />
 | 
			
		||||
    [Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.密码不能为空))]
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,7 @@ namespace NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     请求:密码登录
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record LoginByPwdReq : DataAbstraction
 | 
			
		||||
public sealed record LoginByPwdReq : DataAbstraction
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     用户名、手机号、邮箱
 | 
			
		||||
 
 | 
			
		||||
@@ -5,4 +5,4 @@ namespace NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     请求:短信登录
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record LoginBySmsReq : VerifySmsCodeReq;
 | 
			
		||||
public sealed record LoginBySmsReq : VerifySmsCodeReq;
 | 
			
		||||
@@ -3,7 +3,7 @@ namespace NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     响应:密码登录
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record LoginRsp : DataAbstraction
 | 
			
		||||
public sealed record LoginRsp : DataAbstraction
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     访问令牌
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     请求:注册用户
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record RegisterUserReq : Sys_User
 | 
			
		||||
public sealed record RegisterUserReq : Sys_User
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     密码
 | 
			
		||||
@@ -15,7 +15,7 @@ public record RegisterUserReq : Sys_User
 | 
			
		||||
    [Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.密码不能为空))]
 | 
			
		||||
    [Password]
 | 
			
		||||
    [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
 | 
			
		||||
    public virtual string PasswordText { get; init; }
 | 
			
		||||
    public string PasswordText { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     角色编号列表
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@ namespace NetAdmin.Domain.Dto.Sys.User;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     响应:当前用户信息
 | 
			
		||||
/// </summary>
 | 
			
		||||
public record UserInfoRsp : QueryUserRsp, IRegister
 | 
			
		||||
public sealed record UserInfoRsp : QueryUserRsp, IRegister
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc cref="Sys_User.Dept" />
 | 
			
		||||
    public override QueryDeptRsp Dept { get; init; }
 | 
			
		||||
 
 | 
			
		||||
@@ -18,7 +18,7 @@ public sealed record SqlCommandAfterEvent : SqlCommandBeforeEvent
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     耗时(单位:微秒)
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public long ElapsedMicroseconds { get; set; }
 | 
			
		||||
    public long ElapsedMicroseconds { get; init; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public override string ToString()
 | 
			
		||||
 
 | 
			
		||||
@@ -5,19 +5,11 @@ namespace NetAdmin.Host.BackgroundRunning;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     轮询工作
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class PollingWork<TWorkData> : WorkBase<TWorkData>
 | 
			
		||||
public abstract class PollingWork<TWorkData>(TWorkData workData) : WorkBase<TWorkData>
 | 
			
		||||
    where TWorkData : DataAbstraction
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="PollingWork{TWorkData}" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected PollingWork(TWorkData workData)
 | 
			
		||||
    {
 | 
			
		||||
        WorkData = workData;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     工作数据
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected TWorkData WorkData { get; }
 | 
			
		||||
    protected TWorkData WorkData => workData;
 | 
			
		||||
}
 | 
			
		||||
@@ -54,7 +54,7 @@ public abstract class WorkBase<TLogger>
 | 
			
		||||
    ///     通用工作流
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
 | 
			
		||||
    protected async ValueTask WorkflowAsync(bool singleInstance, CancellationToken cancelToken)
 | 
			
		||||
    protected virtual async ValueTask WorkflowAsync(bool singleInstance, CancellationToken cancelToken)
 | 
			
		||||
    {
 | 
			
		||||
        if (singleInstance) {
 | 
			
		||||
            // 加锁
 | 
			
		||||
 
 | 
			
		||||
@@ -6,20 +6,12 @@ namespace NetAdmin.Host.Controllers;
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     控制器基类
 | 
			
		||||
/// </summary>
 | 
			
		||||
public abstract class ControllerBase<TCache, TService> : IDynamicApiController
 | 
			
		||||
public abstract class ControllerBase<TCache, TService>(TCache cache) : IDynamicApiController
 | 
			
		||||
    where TCache : ICache<IDistributedCache, TService> //
 | 
			
		||||
    where TService : IService
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="ControllerBase{TCache, TService}" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected ControllerBase(TCache cache)
 | 
			
		||||
    {
 | 
			
		||||
        Cache = cache;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     关联的缓存
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    protected TCache Cache { get; }
 | 
			
		||||
    protected TCache Cache => cache;
 | 
			
		||||
}
 | 
			
		||||
@@ -24,8 +24,11 @@ public abstract class ApiResultHandler<T>
 | 
			
		||||
    public IActionResult OnException(ExceptionContext context, ExceptionMetadata metadata)
 | 
			
		||||
    {
 | 
			
		||||
        var lineException = context.Exception switch { NetAdminException ex => ex, _ => null };
 | 
			
		||||
        var errorCode = lineException?.Code ?? ErrorCodes.Unhandled;
 | 
			
		||||
        var result = RestfulResult(errorCode, metadata.Data, lineException?.Message ?? errorCode.ResDesc<ErrorCodes>());
 | 
			
		||||
        var errorCode     = lineException?.Code ?? ErrorCodes.Unhandled;
 | 
			
		||||
        var result = RestfulResult(errorCode, metadata.Data
 | 
			
		||||
                      ,                       lineException is NetAdminValidateException vEx
 | 
			
		||||
                                       ? vEx.ValidateResults
 | 
			
		||||
                                       : lineException?.Message ?? errorCode.ResDesc<ErrorCodes>());
 | 
			
		||||
 | 
			
		||||
        SetErrorCodeToHeader(context.HttpContext, errorCode);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ public static class Numbers
 | 
			
		||||
    public const long DIC_CATALOG_ID_GEO_AREA   = 379794295185413; // 字典目录编号-行政区划字典
 | 
			
		||||
    public const int  HEART_TIMEOUT_SECS        = 600;             // 心跳超时时间
 | 
			
		||||
    public const int  HTTP_STATUS_BIZ_FAIL      = 900;             // Http状态码-业务异常
 | 
			
		||||
    public const int  JOB_TIMEOUT_SECS          = 600;             // 作业超时时间
 | 
			
		||||
    public const int  QUERY_DEF_PAGE_SIZE       = 20;              // 分页查询默认的页容量
 | 
			
		||||
    public const int  QUERY_LIMIT               = 100;             // 非分页查询允许返回的最大条数
 | 
			
		||||
    public const int  QUERY_MAX_PAGE_NO         = 1000;            // 分页查询允许最大的页码
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,18 @@
 | 
			
		||||
namespace NetAdmin.Infrastructure.Exceptions;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     验证失败异常
 | 
			
		||||
/// </summary>
 | 
			
		||||
/// <remarks>
 | 
			
		||||
///     手动调用模型验证方法抛出
 | 
			
		||||
/// </remarks>
 | 
			
		||||
#pragma warning disable RCS1194
 | 
			
		||||
public sealed class NetAdminValidateException(Dictionary<string, string[]> validateResults)
 | 
			
		||||
    : NetAdminException(ErrorCodes.InvalidInput)
 | 
			
		||||
#pragma warning restore RCS1194
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     验证结果
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Dictionary<string, string[]> ValidateResults { get; } = validateResults;
 | 
			
		||||
}
 | 
			
		||||
@@ -13,25 +13,21 @@ public static class HttpRequestPartExtensions
 | 
			
		||||
    public static HttpRequestPart SetLog<T>(this HttpRequestPart me, ILogger<T> logger
 | 
			
		||||
                                          , Func<string, string> bodyHandle = null)
 | 
			
		||||
    {
 | 
			
		||||
        #pragma warning disable S1172
 | 
			
		||||
 | 
			
		||||
        Task RequestHandle(HttpClient _, HttpRequestMessage req)
 | 
			
		||||
        Task RequestHandleAsync(HttpClient _, HttpRequestMessage req)
 | 
			
		||||
        {
 | 
			
		||||
            return req.LogAsync(logger);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Task ExceptionHandle(HttpClient _, HttpResponseMessage rsp, string errors)
 | 
			
		||||
        Task ExceptionHandleAsync(HttpClient _, HttpResponseMessage rsp, string errors)
 | 
			
		||||
        {
 | 
			
		||||
            return rsp.LogExceptionAsync(errors, logger, bodyHandle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Task ResponseHandle(HttpClient _, HttpResponseMessage rsp)
 | 
			
		||||
        Task ResponseHandleAsync(HttpClient _, HttpResponseMessage rsp)
 | 
			
		||||
        {
 | 
			
		||||
            return rsp.LogAsync(logger, bodyHandle);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        #pragma warning restore S1172
 | 
			
		||||
 | 
			
		||||
        return me.OnRequesting(RequestHandle).OnResponsing(ResponseHandle).OnException(ExceptionHandle);
 | 
			
		||||
        return me.OnRequesting(RequestHandleAsync).OnResponsing(ResponseHandleAsync).OnException(ExceptionHandleAsync);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,8 @@ namespace NetAdmin.Infrastructure.Extensions;
 | 
			
		||||
/// </summary>
 | 
			
		||||
public static class StringExtensions
 | 
			
		||||
{
 | 
			
		||||
    private static readonly Regex _regex = new("Options$");
 | 
			
		||||
    private static readonly Regex _regex  = new("Options$");
 | 
			
		||||
    private static readonly Regex _regex2 = new("Async$");
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     object -> json
 | 
			
		||||
@@ -23,6 +24,16 @@ public static class StringExtensions
 | 
			
		||||
        return me.Object(toType, GlobalStatic.JsonSerializerOptions);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     去掉尾部字符串“Async”
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    #pragma warning disable RCS1047, ASA002, VSTHRD200
 | 
			
		||||
    public static string TrimEndAsync(this string me)
 | 
			
		||||
        #pragma warning restore VSTHRD200, ASA002, RCS1047
 | 
			
		||||
    {
 | 
			
		||||
        return _regex2.Replace(me, string.Empty);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     去掉尾部字符串“Options”
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -6,14 +6,14 @@
 | 
			
		||||
    <Import Project="$(SolutionDir)/build/copy.pkg.xml.comment.files.targets"/>
 | 
			
		||||
    <Import Project="$(SolutionDir)/build/prebuild.targets"/>
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <PackageReference Include="Cronos" Version="0.8.2"/>
 | 
			
		||||
        <PackageReference Include="Cronos" Version="0.8.3"/>
 | 
			
		||||
        <PackageReference Include="FreeSql.DbContext.NS" Version="3.2.810-preview20231229-ns1"/>
 | 
			
		||||
        <PackageReference Include="FreeSql.Provider.Sqlite.NS" Version="3.2.810-preview20231229-ns1"/>
 | 
			
		||||
        <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.9.1.24"/>
 | 
			
		||||
        <PackageReference Include="Furion.Extras.ObjectMapper.Mapster.NS" Version="4.9.1.24-ns1"/>
 | 
			
		||||
        <PackageReference Include="Furion.Pure.NS" Version="4.9.1.24-ns1"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="8.0.1"/>
 | 
			
		||||
        <PackageReference Include="Minio" Version="6.0.1"/>
 | 
			
		||||
        <PackageReference Include="Minio" Version="6.0.2"/>
 | 
			
		||||
        <PackageReference Include="NSExt" Version="2.0.11"/>
 | 
			
		||||
        <PackageReference Include="RedLock.net" Version="2.3.2"/>
 | 
			
		||||
        <PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0"/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
using NetAdmin.Application.Modules;
 | 
			
		||||
using NetAdmin.Domain.Dto.Dependency;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.Job;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.JobRecord;
 | 
			
		||||
 | 
			
		||||
namespace NetAdmin.SysComponent.Application.Modules.Sys;
 | 
			
		||||
 | 
			
		||||
@@ -13,6 +14,16 @@ public interface IJobModule : ICrudModule<CreateJobReq, QueryJobRsp // 创建类
 | 
			
		||||
  , DelReq                                                          // 删除类型
 | 
			
		||||
>
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     获取单个作业记录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<QueryJobRecordRsp> RecordGetAsync(QueryJobRecordReq req);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     分页查询作业记录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<PagedQueryRsp<QueryJobRecordRsp>> RecordPagedQueryAsync(PagedQueryReq<QueryJobRecordReq> req);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     启用/禁用作业
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -18,4 +18,9 @@ public interface IJobService : IService, IJobModule
 | 
			
		||||
    ///     获取下一个要执行的计划作业
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<QueryJobRsp> GetNextJobAsync();
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     释放卡死的任务
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task<int> ReleaseStuckTaskAsync();
 | 
			
		||||
}
 | 
			
		||||
@@ -102,6 +102,9 @@ public sealed class JobRecordService(DefaultRepository<Sys_JobRecord> rpo) //
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
                       , a => a.JobId == req.Keywords.Int64Try(0) || a.Id == req.Keywords.Int64Try(0))
 | 
			
		||||
                     .OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
 | 
			
		||||
        if (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true) {
 | 
			
		||||
            ret = ret.OrderByDescending(a => a.Id);
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ using NetAdmin.Application.Services;
 | 
			
		||||
using NetAdmin.Domain.DbMaps.Sys;
 | 
			
		||||
using NetAdmin.Domain.Dto.Dependency;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.Job;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.JobRecord;
 | 
			
		||||
using NetAdmin.Domain.Enums.Sys;
 | 
			
		||||
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
 | 
			
		||||
using DataType = FreeSql.DataType;
 | 
			
		||||
@@ -11,7 +12,7 @@ using DataType = FreeSql.DataType;
 | 
			
		||||
namespace NetAdmin.SysComponent.Application.Services.Sys;
 | 
			
		||||
 | 
			
		||||
/// <inheritdoc cref="IJobService" />
 | 
			
		||||
public sealed class JobService(DefaultRepository<Sys_Job> rpo) //
 | 
			
		||||
public sealed class JobService(DefaultRepository<Sys_Job> rpo, IJobRecordService jobRecordService) //
 | 
			
		||||
    : RepositoryService<Sys_Job, IJobService>(rpo), IJobService
 | 
			
		||||
{
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
@@ -134,6 +135,28 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo) //
 | 
			
		||||
        return ret.Adapt<IEnumerable<QueryJobRsp>>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryJobRecordRsp> RecordGetAsync(QueryJobRecordReq req)
 | 
			
		||||
    {
 | 
			
		||||
        req.ThrowIfInvalid();
 | 
			
		||||
        return jobRecordService.GetAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<PagedQueryRsp<QueryJobRecordRsp>> RecordPagedQueryAsync(PagedQueryReq<QueryJobRecordReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return jobRecordService.PagedQueryAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<int> ReleaseStuckTaskAsync()
 | 
			
		||||
    {
 | 
			
		||||
        return Rpo.UpdateDiy.Set(a => a.Status == JobStatues.Idle)
 | 
			
		||||
                  .Where(a => a.Status       == JobStatues.Running &&
 | 
			
		||||
                              a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.JOB_TIMEOUT_SECS))
 | 
			
		||||
                  .ExecuteAffrowsAsync();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task SetEnabledAsync(UpdateJobReq req)
 | 
			
		||||
    {
 | 
			
		||||
@@ -162,7 +185,8 @@ public sealed class JobService(DefaultRepository<Sys_Job> rpo) //
 | 
			
		||||
 | 
			
		||||
    private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req, bool orderByRandom = false)
 | 
			
		||||
    {
 | 
			
		||||
        var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
        var ret = Rpo.Select.Include(a => a.User)
 | 
			
		||||
                     .WhereDynamicFilter(req.DynamicFilter)
 | 
			
		||||
                     .WhereDynamic(req.Filter)
 | 
			
		||||
                     .WhereIf( //
 | 
			
		||||
                         req.Keywords?.Length > 0
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,7 @@ public interface IUserCache : ICache<IDistributedCache, IUserService>, IUserModu
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     删除缓存 RegisterAsync
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Task RemoveRegisterAsync(RegisterUserReq userReq);
 | 
			
		||||
    Task RemoveRegisterAsync(RegisterUserReq req);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     删除缓存 ResetPasswordAsync
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
using NetAdmin.Cache;
 | 
			
		||||
using NetAdmin.Domain.Dto.Dependency;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.Job;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.JobRecord;
 | 
			
		||||
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
 | 
			
		||||
using NetAdmin.SysComponent.Cache.Sys.Dependency;
 | 
			
		||||
 | 
			
		||||
@@ -52,6 +53,18 @@ public sealed class JobCache(IDistributedCache cache, IJobService service)
 | 
			
		||||
        return Service.QueryAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<QueryJobRecordRsp> RecordGetAsync(QueryJobRecordReq req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.RecordGetAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task<PagedQueryRsp<QueryJobRecordRsp>> RecordPagedQueryAsync(PagedQueryReq<QueryJobRecordReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Service.RecordPagedQueryAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task SetEnabledAsync(UpdateJobReq req)
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -120,7 +120,7 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc />
 | 
			
		||||
    public Task RemoveRegisterAsync(RegisterUserReq userReq)
 | 
			
		||||
    public Task RemoveRegisterAsync(RegisterUserReq req)
 | 
			
		||||
    {
 | 
			
		||||
        throw new NotImplementedException();
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
using NetAdmin.Domain.Dto.Dependency;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.Job;
 | 
			
		||||
using NetAdmin.Domain.Dto.Sys.JobRecord;
 | 
			
		||||
using NetAdmin.Host.Attributes;
 | 
			
		||||
using NetAdmin.Host.Controllers;
 | 
			
		||||
using NetAdmin.SysComponent.Application.Modules.Sys;
 | 
			
		||||
@@ -73,6 +74,22 @@ public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, I
 | 
			
		||||
        return Cache.QueryAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     获取单个作业记录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Task<QueryJobRecordRsp> RecordGetAsync(QueryJobRecordReq req)
 | 
			
		||||
    {
 | 
			
		||||
        return Cache.RecordGetAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     分页查询作业记录
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public Task<PagedQueryRsp<QueryJobRecordRsp>> RecordPagedQueryAsync(PagedQueryReq<QueryJobRecordReq> req)
 | 
			
		||||
    {
 | 
			
		||||
        return Cache.RecordPagedQueryAsync(req);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     启用/禁用作业
 | 
			
		||||
    /// </summary>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,11 @@ public static class ServiceCollectionExtensions
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public static IServiceCollection AddSchedules(this IServiceCollection me)
 | 
			
		||||
    {
 | 
			
		||||
        return me.AddSchedule( //
 | 
			
		||||
            builder => builder //
 | 
			
		||||
                .AddJob<ScheduledJob>(false, Triggers.PeriodSeconds(5).SetRunOnStart(true)));
 | 
			
		||||
        return App.WebHostEnvironment.EnvironmentName != Environments.Production
 | 
			
		||||
            ? me
 | 
			
		||||
            : me.AddSchedule(      //
 | 
			
		||||
                builder => builder //
 | 
			
		||||
                           .AddJob<ScheduledJob>(false,     Triggers.PeriodSeconds(5).SetRunOnStart(true))
 | 
			
		||||
                           .AddJob<FreeScheduledJob>(false, Triggers.PeriodMinutes(1).SetRunOnStart(true)));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,42 @@
 | 
			
		||||
using Furion.Schedule;
 | 
			
		||||
using NetAdmin.Host.BackgroundRunning;
 | 
			
		||||
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
 | 
			
		||||
 | 
			
		||||
namespace NetAdmin.SysComponent.Host.Jobs;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
///     释放计划作业
 | 
			
		||||
/// </summary>
 | 
			
		||||
public sealed class FreeScheduledJob : WorkBase<FreeScheduledJob>, IJob
 | 
			
		||||
{
 | 
			
		||||
    private readonly IJobService _jobService;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     Initializes a new instance of the <see cref="FreeScheduledJob" /> class.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public FreeScheduledJob()
 | 
			
		||||
    {
 | 
			
		||||
        _jobService = ServiceProvider.GetService<IJobService>();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     具体处理逻辑
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <param name="context">作业执行前上下文</param>
 | 
			
		||||
    /// <param name="stoppingToken">取消任务 Token</param>
 | 
			
		||||
    /// <exception cref="NetAdminGetLockerException">加锁失败异常</exception>
 | 
			
		||||
    public async Task ExecuteAsync(JobExecutingContext context, CancellationToken stoppingToken)
 | 
			
		||||
    {
 | 
			
		||||
        await WorkflowAsync(true, stoppingToken).ConfigureAwait(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    ///     通用工作流
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    /// <exception cref="NotImplementedException">NotImplementedException</exception>
 | 
			
		||||
    /// <exception cref="ArgumentOutOfRangeException">ArgumentOutOfRangeException</exception>
 | 
			
		||||
    protected override async ValueTask WorkflowAsync(CancellationToken cancelToken)
 | 
			
		||||
    {
 | 
			
		||||
        _ = await _jobService.ReleaseStuckTaskAsync().ConfigureAwait(false);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -87,7 +87,7 @@ public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
 | 
			
		||||
                                            {
 | 
			
		||||
                                                Duration       = sw.ElapsedMilliseconds
 | 
			
		||||
                                              , HttpMethod     = job.HttpMethod
 | 
			
		||||
                                              , HttpStatusCode = rsp.StatusCode
 | 
			
		||||
                                              , HttpStatusCode = (int)rsp.StatusCode
 | 
			
		||||
                                              , JobId          = job.Id
 | 
			
		||||
                                              , RequestBody    = job.RequestBody
 | 
			
		||||
                                              , RequestHeader  = _requestHeader
 | 
			
		||||
 
 | 
			
		||||
@@ -3,9 +3,9 @@
 | 
			
		||||
        <ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
 | 
			
		||||
    </ItemGroup>
 | 
			
		||||
    <ItemGroup>
 | 
			
		||||
        <PackageReference Include="xunit" Version="2.6.6"/>
 | 
			
		||||
        <PackageReference Include="xunit" Version="2.7.0"/>
 | 
			
		||||
        <PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.1"/>
 | 
			
		||||
        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
 | 
			
		||||
        <PackageReference Include="xunit.runner.visualstudio" Version="2.5.7">
 | 
			
		||||
            <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
 | 
			
		||||
            <PrivateAssets>all</PrivateAssets>
 | 
			
		||||
        </PackageReference>
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user