mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-06-19 18:28:17 +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