feat: 计划作业 (#87)

This commit is contained in:
nsnail 2024-02-02 14:05:39 +08:00 committed by GitHub
parent 473b0c26f2
commit 8293ec0297
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
70 changed files with 2171 additions and 14 deletions

View File

@ -65,6 +65,7 @@
硕士
离异
私信
空闲
等于
等待发送
系统模块
@ -79,6 +80,7 @@
调试
跟踪
身份证
运行
重设密码
链接
错误

View File

@ -7,6 +7,7 @@ XML注释文件不存在
事务已提交
人机校验请求不能为空
人机验证未通过
作业名称不能为空
允许的文件大小
允许的文件格式
区号电话号码分机号
@ -23,6 +24,7 @@ XML注释文件不存在
开始事务
手机号码不正确
手机号码不能为空
接口编码不存在
支付宝账号
数据库同步开始
数据库服务器时钟偏移
@ -39,7 +41,10 @@ XML注释文件不存在
旧手机号码不正确
旧手机号码验证码不正确
时间戳缺失或误差过大
时间表达式
时间计划不能为空
未指定部门
未获取到待执行任务
模块名称不能为空
模块说明不能为空
消息主题不能为空
@ -52,6 +57,7 @@ XML注释文件不存在
用户名长度4位以上
用户头像不能为空
用户档案不能为空
用户编号不存在
目标设备不能为空
短信验证请求不能为空
站内信不存在
@ -68,7 +74,9 @@ XML注释文件不存在
该角色下存在用户
该部门下存在子部门
该部门下存在用户
请求地址不能为空
请求对象不能为空
请求方法不正确
请联系管理员激活账号
读取用户令牌出错
账号不能为空

View File

@ -0,0 +1,13 @@
[
{
"Enabled": true,
"ExecutionCron": "* * * * *",
"HttpMethod": 3,
"JobName": "HTTP 请求测试",
"NextExecTime": "2020/9/13 12:26:40",
"NextTimeId": 1600000000,
"RequestUrl": "https://httpbin.org/ip",
"Status": 1,
"UserId": 370942943322181,
}
]

View File

@ -85,6 +85,17 @@
"Title": "系统设置",
"Type": 1
},
{
"Component": "sys/job",
"Icon": "sc-icon-ScheduledJob",
"Id": 510067557638158,
"Name": "sys-job",
"ParentId": 485278637670422,
"Path": "/sys/job",
"Sort": 99,
"Title": "计划作业",
"Type": 1
},
{
"Component": "sys/dic",
"Icon": "sc-icon-dic",
@ -92,7 +103,7 @@
"Name": "sys-dic",
"ParentId": 485278637670422,
"Path": "/sys/dic",
"Sort": 99,
"Sort": 98,
"Title": "字典管理",
"Type": 1
},
@ -103,7 +114,7 @@
"Name": "sys-msg",
"ParentId": 485278637670422,
"Path": "/sys/msg",
"Sort": 98,
"Sort": 97,
"Title": "消息管理",
"Type": 1,
},
@ -114,7 +125,7 @@
"Name": "sys-api",
"ParentId": 485278637670422,
"Path": "/sys/api",
"Sort": 97,
"Sort": 96,
"Title": "接口管理",
"Type": 1
},
@ -125,7 +136,7 @@
"Name": "sys-cache",
"ParentId": 485278637670422,
"Path": "/sys/cache",
"Sort": 96,
"Sort": 95,
"Title": "缓存管理",
"Type": 1
},
@ -136,7 +147,7 @@
"Name": "sys-about",
"ParentId": 485278637670422,
"Path": "/sys/about",
"Sort": 95,
"Sort": 94,
"Title": "版本信息",
"Type": 1,
},

View File

@ -19,11 +19,11 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.9.0">
<PackageReference Include="Roslynator.Analyzers" Version="4.10.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.17.0.82934">
<PackageReference Include="SonarAnalyzer.CSharp" Version="9.19.0.84025">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -15,7 +15,7 @@
]
},
"jetbrains.resharper.globaltools": {
"version": "2023.3.2",
"version": "2023.3.3",
"commands": [
"jb"
]

View File

@ -0,0 +1,15 @@
using System.Text.RegularExpressions;
Console.WriteLine(string.Join(Environment.NewLine
, Regex
.Matches(File.ReadAllText(@"../assets/res/Ln.resx")
, "data name=\"(.*?)\"")
.Select(x => x.Groups[1].Value)
.Where(x => !Directory
.GetFiles(@"../src/backend/", "*.cs"
, new EnumerationOptions {
RecurseSubdirectories = true
})
.Select(File.ReadAllText)
.Any(y => y.Contains(x)))));
Console.ReadKey();

View File

@ -2,6 +2,7 @@ using NetAdmin.BizServer.Host;
using NetAdmin.BizServer.Host.Extensions;
using NetAdmin.Host.Extensions;
using NetAdmin.Host.Middlewares;
using NetAdmin.SysComponent.Host.Extensions;
using Spectre.Console.Cli;
using ValidationResult = Spectre.Console.ValidationResult;
#if !DEBUG
@ -62,6 +63,7 @@ namespace NetAdmin.BizServer.Host
.AddCorsAccessor() // 添加支持跨域访问
.AddContextUser() // 添加上下文用户
.AddRedisCache() // 添加 Redis 缓存
.AddSchedules() // 添加计划任务
// IMvcBuilder
.AddControllers() // 添加控制器

View File

@ -4,6 +4,6 @@
<ProjectReference Include="../NetAdmin.BizServer.Host/NetAdmin.BizServer.Host.csproj"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0-release-23619-01"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0-preview-24080-01"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Api;
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 接口编码验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class ApiIdAttribute : ValidationAttribute
{
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var service = App.GetService(App.EffectiveTypes.Single(
x => x.FullName == "NetAdmin.SysComponent.Cache.Sys.Dependency.IApiCache"));
var req = new QueryReq<QueryApiReq> { Filter = new QueryApiReq { Id = value as string } };
var method = service.GetType().GetMethod("ExistAsync");
var exist = ((Task<bool>)method!.Invoke(service, [req]))!.ConfigureAwait(false).GetAwaiter().GetResult();
return !exist ? new ValidationResult(Ln.) : ValidationResult.Success;
}
}

View File

@ -0,0 +1,18 @@
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 时间表达式验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class CronAttribute : RegexAttribute
{
/// <summary>
/// Initializes a new instance of the <see cref="CronAttribute" /> class.
/// </summary>
public CronAttribute() //
: base(Chars.RGX_CRON)
{
ErrorMessageResourceName = nameof(Ln.);
ErrorMessageResourceType = typeof(Ln);
}
}

View File

@ -0,0 +1,24 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.User;
namespace NetAdmin.Domain.Attributes.DataValidation;
/// <summary>
/// 用户编号验证器
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter)]
public sealed class UserIdAttribute : ValidationAttribute
{
/// <inheritdoc />
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var service = App.GetService(App.EffectiveTypes.Single(
x => x.FullName == "NetAdmin.SysComponent.Cache.Sys.Dependency.IUserCache"));
var req = new QueryReq<QueryUserReq> { Filter = new QueryUserReq { Id = (long)value! } };
var method = service.GetType().GetMethod("ExistAsync");
var exist = ((Task<bool>)method!.Invoke(service, [req]))!.ConfigureAwait(false).GetAwaiter().GetResult();
return !exist ? new ValidationResult(Ln.) : ValidationResult.Success;
}
}

View File

@ -5,6 +5,17 @@ namespace NetAdmin.Domain;
/// </summary>
public abstract record DataAbstraction
{
/// <summary>
/// 如果数据校验失败,抛出异常
/// </summary>
/// <exception cref="NetAdminInvalidInputException">NetAdminInvalidInputException</exception>
public void ThrowIfInvalid()
{
if (!this.TryValidate().IsValid) {
throw new NetAdminInvalidInputException(Ln.);
}
}
/// <inheritdoc />
public override string ToString()
{

View File

@ -0,0 +1,107 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.Enums.Sys;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 计划作业表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_Job))]
public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore]
[Column]
public virtual bool Enabled { get; init; }
/// <summary>
/// 执行时间计划
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
public virtual string ExecutionCron { get; init; }
/// <summary>
/// 请求方法
/// </summary>
[JsonIgnore]
[Column]
public virtual HttpMethods HttpMethod { get; init; }
/// <summary>
/// 作业名称
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_63)]
public virtual string JobName { get; init; }
/// <summary>
/// 上次执行时间
/// </summary>
[JsonIgnore]
[Column]
public virtual DateTime? LastExecTime { get; init; }
/// <summary>
/// 上次执行状态
/// </summary>
[JsonIgnore]
[Column]
public virtual HttpStatusCode? LastStatusCode { get; init; }
/// <summary>
/// 下次执行时间
/// </summary>
[JsonIgnore]
[Column]
public virtual DateTime? NextExecTime { get; init; }
/// <summary>
/// 下次执行时间编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long? NextTimeId { get; init; }
/// <summary>
/// 请求体
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string RequestBody { get; init; }
/// <summary>
/// 请求头
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string RequestHeader { get; init; }
/// <summary>
/// 请求的网络地址
/// </summary>
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string RequestUrl { get; init; }
/// <summary>
/// 作业状态
/// </summary>
[JsonIgnore]
[Column]
public virtual JobStatues Status { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore]
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
public virtual string Summary { get; init; }
/// <summary>
/// 执行用户编号
/// </summary>
[JsonIgnore]
[Column]
public virtual long UserId { get; init; }
}

View File

@ -0,0 +1,82 @@
using NetAdmin.Domain.DbMaps.Dependency;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.DbMaps.Sys;
/// <summary>
/// 计划作业执行记录表
/// </summary>
[Table(Name = Chars.FLG_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))]
[Index($"idx_{{tablename}}_{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)]
public record Sys_JobRecord : LiteImmutableEntity
{
/// <summary>
/// 执行耗时(毫秒)
/// </summary>
[Column]
[JsonIgnore]
public virtual long Duration { get; init; }
/// <summary>
/// 请求方法
/// </summary>
[JsonIgnore]
[Column]
public virtual HttpMethods HttpMethod { get; init; }
/// <summary>
/// HTTP 状态码
/// </summary>
[Column]
[JsonIgnore]
public virtual HttpStatusCode HttpStatusCode { get; init; }
/// <summary>
/// 作业编号
/// </summary>
[Column]
[JsonIgnore]
public long JobId { get; init; }
/// <summary>
/// 请求体
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string RequestBody { get; init; }
/// <summary>
/// 请求头
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string RequestHeader { get; init; }
/// <summary>
/// 请求的网络地址
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_127)]
[JsonIgnore]
public virtual string RequestUrl { get; init; }
/// <summary>
/// 响应体
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ResponseBody { get; init; }
/// <summary>
/// 响应头
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_255)]
[JsonIgnore]
public virtual string ResponseHeader { get; init; }
/// <summary>
/// 执行时间编号
/// </summary>
[Column]
[JsonIgnore]
public long TimeId { get; set; }
}

View File

@ -0,0 +1,65 @@
using NetAdmin.Domain.Attributes.DataValidation;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Enums.Sys;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.Dto.Sys.Job;
/// <summary>
/// 请求:创建计划作业
/// </summary>
public record CreateJobReq : Sys_Job
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; } = true;
/// <inheritdoc cref="Sys_Job.ExecutionCron" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Cron]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.时间计划不能为空))]
public override string ExecutionCron { get; init; }
/// <inheritdoc cref="Sys_Job.HttpMethod" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[EnumDataType(typeof(HttpMethods), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))]
public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc cref="Sys_Job.JobName" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.作业名称不能为空))]
public override string JobName { get; init; }
/// <inheritdoc />
public override long? NextTimeId { get; init; }
/// <inheritdoc cref="Sys_Job.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }
/// <summary>
/// 请求头
/// </summary>
public Dictionary<string, string> RequestHeaders { get; init; }
/// <inheritdoc cref="Sys_Job.RequestUrl" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.请求地址不能为空))]
[Url(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.网络地址不正确))]
public override string RequestUrl { get; init; }
/// <inheritdoc />
public override JobStatues Status { get; init; } = JobStatues.Idle;
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
/// <inheritdoc cref="Sys_Job.UserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[Range(1, long.MaxValue, ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.用户编号不存在))]
[UserId]
public override long UserId { get; init; }
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Job;
/// <summary>
/// 请求:查询计划作业
/// </summary>
public sealed record QueryJobReq : Sys_Job
{
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -0,0 +1,80 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Enums.Sys;
using HttpMethods = NetAdmin.Domain.Enums.HttpMethods;
namespace NetAdmin.Domain.Dto.Sys.Job;
/// <summary>
/// 响应:查询计划作业
/// </summary>
public sealed record QueryJobRsp : Sys_Job
{
/// <inheritdoc cref="IFieldCreatedTime.CreatedTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override DateTime CreatedTime { get; init; }
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="Sys_Job.ExecutionCron" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string ExecutionCron { get; init; }
/// <inheritdoc cref="Sys_Job.HttpMethod" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override HttpMethods HttpMethod { get; init; }
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="Sys_Job.JobName" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string JobName { get; init; }
/// <inheritdoc cref="Sys_Job.LastExecTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? LastExecTime { get; init; }
/// <inheritdoc cref="Sys_Job.LastStatusCode" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override HttpStatusCode? LastStatusCode { get; init; }
/// <inheritdoc cref="Sys_Job.NextExecTime" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override DateTime? NextExecTime { get; init; }
/// <inheritdoc cref="Sys_Job.NextTimeId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override long? NextTimeId { get; init; }
/// <inheritdoc cref="Sys_Job.RequestBody" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestBody { get; init; }
/// <inheritdoc cref="Sys_Job.RequestHeader" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestHeader { get; init; }
/// <inheritdoc cref="Sys_Job.RequestUrl" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string RequestUrl { get; init; }
/// <inheritdoc cref="Sys_Job.Status" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override JobStatues Status { get; init; }
/// <inheritdoc cref="IFieldSummary.Summary" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string Summary { get; init; }
/// <inheritdoc cref="Sys_Job.UserId" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long UserId { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,13 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
namespace NetAdmin.Domain.Dto.Sys.Job;
/// <summary>
/// 请求:更新计划作业
/// </summary>
public sealed record UpdateJobReq : CreateJobReq
{
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,8 @@
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
/// <summary>
/// 请求:创建计划作业执行记录
/// </summary>
public record CreateJobRecordReq : Sys_JobRecord;

View File

@ -0,0 +1,14 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
/// <summary>
/// 请求:查询计划作业执行记录
/// </summary>
public sealed record QueryJobRecordReq : Sys_JobRecord
{
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
/// <summary>
/// 响应:查询计划作业执行记录
/// </summary>
public sealed record QueryJobRecordRsp : Sys_JobRecord
{
/// <inheritdoc cref="IFieldPrimary{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
}

View File

@ -0,0 +1,6 @@
namespace NetAdmin.Domain.Dto.Sys.JobRecord;
/// <summary>
/// 请求:更新计划作业执行记录
/// </summary>
public sealed record UpdateJobRecordReq : CreateJobRecordReq { }

View File

@ -0,0 +1,69 @@
namespace NetAdmin.Domain.Enums;
/// <summary>
/// HTTP 请求方法
/// </summary>
[Export]
public enum HttpMethods
{
/// <summary>
/// Connect
/// </summary>
Connect = 1
,
/// <summary>
/// Delete
/// </summary>
Delete = 2
,
/// <summary>
/// Get
/// </summary>
Get = 3
,
/// <summary>
/// Head
/// </summary>
Head = 4
,
/// <summary>
/// Options
/// </summary>
Options = 5
,
/// <summary>
/// Patch
/// </summary>
Patch = 6
,
/// <summary>
/// Post
/// </summary>
Post = 7
,
/// <summary>
/// Put
/// </summary>
Put = 8
,
/// <summary>
/// Trace
/// </summary>
Trace = 9
}

View File

@ -0,0 +1,22 @@
namespace NetAdmin.Domain.Enums.Sys;
/// <summary>
/// 计划作业状态
/// </summary>
[Export]
public enum JobStatues
{
/// <summary>
/// 空闲
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.空闲))]
Idle = 1
,
/// <summary>
/// 运行
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.运行))]
Running = 2
}

View File

@ -11,7 +11,9 @@ namespace NetAdmin.Infrastructure.Constant;
public static class Chars
{
public const string FLG_ACCESS_TOKEN = "ACCESS-TOKEN";
public const string FLG_ACCESS_TOKEN_HEADER_KEY = "Authorization";
public const string FLG_APPLICATION_JSON = "application/json";
public const string FLG_AUTH_SCHEMA = "Bearer";
public const string FLG_CONSUL_REG_HOSTNAME = "CONSUL_REG_HOSTNAME";
public const string FLG_CONSUL_REG_PORT = "CONSUL_REG_PORT";
public const string FLG_CONTEXT_MEMBER_INFO = nameof(FLG_CONTEXT_MEMBER_INFO);
@ -67,12 +69,16 @@ public static class Chars
= """!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~""";
public const string FLG_X_ACCESS_TOKEN = "X-ACCESS-TOKEN";
public const string FLG_X_ACCESS_TOKEN_HEADER_KEY = "X-Authorization";
public const string RGX_CERTIFICATE = "^[a-zA-Z0-9-_]+$";
public const string RGX_CHINESE_NAME
= """^(?:赵|钱|孙|李|周|吴|郑|王|冯|陈|褚|卫|蒋|沈|韩|杨|朱|秦|尤|许|何|吕|施|张|孔|曹|严|华|金|魏|陶|姜|戚|谢|邹|喻|柏|水|窦|章|云|苏|潘|葛|奚|范|彭|郎|鲁|韦|昌|马|苗|凤|花|方|俞|任|袁|柳|酆|鲍|史|唐|费|廉|岑|薛|雷|贺|倪|汤|滕|殷|罗|毕|郝|邬|安|常|乐|于|时|傅|皮|卞|齐|康|伍|余|元|卜|顾|孟|平|黄|和|穆|萧|尹|姚|邵|湛|汪|祁|毛|禹|狄|米|贝|明|臧|计|伏|成|戴|谈|宋|茅|庞|熊|纪|舒|屈|项|祝|董|梁|杜|阮|蓝|闵|席|季|麻|强|贾|路|娄|危|江|童|颜|郭|梅|盛|林|刁|钟|徐|邱|骆|高|夏|蔡|田|樊|胡|凌|霍|虞|万|支|柯|昝|管|卢|莫|经|房|裘|缪|干|解|应|宗|丁|宣|贲|邓|郁|单|杭|洪|包|诸|左|石|崔|吉|钮|龚|程|嵇|邢|滑|裴|陆|荣|翁|荀|羊|於|惠|甄|曲|家|封|芮|羿|储|靳|汲|邴|糜|松|井|段|富|巫|乌|焦|巴|弓|牧|隗|山|谷|车|侯|宓|蓬|全|郗|班|仰|秋|仲|伊|宫|宁|仇|栾|暴|甘|钭|厉|戎|祖|武|符|刘|景|詹|束|龙|叶|幸|司|韶|郜|黎|蓟|薄|印|宿|白|怀|蒲|邰|从|鄂|索|咸|籍|赖|卓|蔺|屠|蒙|池|乔|阴|胥|能|苍|双|闻|莘|党|翟|谭|贡|劳|逄|姬|申|扶|堵|冉|宰|郦|雍|郤|璩|桑|桂|濮|牛|寿|通|边|扈|燕|冀|郏|浦|尚|农|温|别|庄|晏|柴|瞿|阎|充|慕|连|茹|习|宦|艾|鱼|容|向|古|易|慎|戈|廖|庾|终|暨|居|衡|步|都|耿|满|弘|匡|国|文|寇|广|禄|阙|东|欧|殳|沃|利|蔚|越|夔|隆|师|巩|厍|聂|晁|勾|敖|融|冷|訾|辛|阚|那|简|饶|空|曾|毋|沙|乜|养|鞠|须|丰|巢|关|蒯|相|查|後|荆|红|游|竺|权|逯|盖|益|桓|公|万俟|司马|上官|欧阳|夏侯|诸葛|闻人|东方|赫连|皇甫|尉迟|公羊|澹台|公冶|宗政|濮阳|淳于|单于|太叔|申屠|公孙|仲孙|轩辕|令狐|钟离|宇文|长孙|慕容|鲜于|闾丘|司徒|司空|亓官|司寇|仉|督|子车|颛孙|端木|巫马|公西|漆雕|乐正|壤驷|公良|拓跋|夹谷|宰父|谷梁|晋|楚|闫|法|汝|鄢|涂|钦|段干|百里|东郭|南门|呼延|归|海|羊舌|微生|岳|帅|缑|亢|况|后|有|琴|梁丘|左丘|东门|西门|商|牟|佘|佴|伯|赏|南宫|墨|哈|谯|笪|年|爱|阳|佟|第五|言|福)[\u4e00-\u9fa5]{1,3}$""";
public const string RGX_CRON
= """^(?:[0-5]?[0-9]|[0-9]|[1-5]?[0-9](?:,[0-5]?[0-9])?|\*|\*/[0-5]?[0-9])\s+(?:[01]?[0-9]|2[0-3]|\*)\s+(?:[0-2]?[0-9]|3[01]|\*)\s+(?:0?[0-9]|1[0-1]|\*)\s+(?:[0-6]|\*)$""";
public const string RGX_EMAIL
= """^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$""";

View File

@ -1,3 +1,5 @@
using DataType = FreeSql.DataType;
namespace NetAdmin.Infrastructure;
/// <summary>
@ -27,6 +29,41 @@ public static class GlobalStatic
/// </summary>
public static string SecretKey => "{6C4922D3-499A-46db-BFC4-0B51A9C4395F}";
/// <summary>
/// SQL 随机排序语法
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
public static string SqlRandomSorting =>
App.GetOptions<DatabaseOptions>().DbType switch {
DataType.MySql => "RAND()"
, DataType.SqlServer => "NEWID()"
, DataType.PostgreSQL => "RANDOM()"
, DataType.Oracle => "DBMS_RANDOM.value"
, DataType.Sqlite => "RANDOM()"
, DataType.OdbcOracle => throw new NotImplementedException()
, DataType.OdbcSqlServer => throw new NotImplementedException()
, DataType.OdbcMySql => throw new NotImplementedException()
, DataType.OdbcPostgreSQL => throw new NotImplementedException()
, DataType.Odbc => throw new NotImplementedException()
, DataType.OdbcDameng => throw new NotImplementedException()
, DataType.MsAccess => throw new NotImplementedException()
, DataType.Dameng => throw new NotImplementedException()
, DataType.OdbcKingbaseES => throw new NotImplementedException()
, DataType.ShenTong => throw new NotImplementedException()
, DataType.KingbaseES => throw new NotImplementedException()
, DataType.Firebird => throw new NotImplementedException()
, DataType.Custom => throw new NotImplementedException()
, DataType.ClickHouse => throw new NotImplementedException()
, DataType.GBase => throw new NotImplementedException()
, DataType.QuestDb => throw new NotImplementedException()
, DataType.Xugu => throw new NotImplementedException()
, DataType.CustomOracle => throw new NotImplementedException()
, DataType.CustomSqlServer => throw new NotImplementedException()
, DataType.CustomMySql => throw new NotImplementedException()
, DataType.CustomPostgreSQL => throw new NotImplementedException()
, _ => throw new NotImplementedException()
};
/// <summary>
/// Json序列化选项
/// </summary>

View File

@ -6,16 +6,17 @@
<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="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="NSExt" Version="2.0.11"/>
<PackageReference Include="RedLock.net" Version="2.3.2"/>
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0"/>
<PackageReference Include="Minio" Version="6.0.1"/>
<PackageReference Include="Yitter.IdGenerator" Version="1.0.14"/>
</ItemGroup>
<ItemGroup>

View File

@ -0,0 +1,20 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Job;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 计划作业模块
/// </summary>
public interface IJobModule : ICrudModule<CreateJobReq, QueryJobRsp // 创建类型
, QueryJobReq, QueryJobRsp // 查询类型
, UpdateJobReq, QueryJobRsp // 修改类型
, DelReq // 删除类型
>
{
/// <summary>
/// 启用/禁用作业
/// </summary>
Task SetEnabledAsync(UpdateJobReq req);
}

View File

@ -0,0 +1,14 @@
using NetAdmin.Application.Modules;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.JobRecord;
namespace NetAdmin.SysComponent.Application.Modules.Sys;
/// <summary>
/// 计划作业执行记录模块
/// </summary>
public interface IJobRecordModule : ICrudModule<CreateJobRecordReq, QueryJobRecordRsp // 创建类型
, QueryJobRecordReq, QueryJobRecordRsp // 查询类型
, UpdateJobRecordReq, QueryJobRecordRsp // 修改类型
, DelReq // 删除类型
>;

View File

@ -17,42 +17,49 @@ public sealed class ApiService(
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<QueryApiRsp> CreateAsync(CreateApiReq req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryApiReq> req)
{
throw new NotImplementedException();
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public Task<QueryApiRsp> GetAsync(QueryApiReq req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryApiRsp>> PagedQueryAsync(PagedQueryReq<QueryApiReq> req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public async Task<IEnumerable<QueryApiRsp>> QueryAsync(QueryReq<QueryApiReq> req)
{
req.ThrowIfInvalid();
var ret = await Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.ToTreeListAsync()
@ -108,6 +115,7 @@ public sealed class ApiService(
/// <inheritdoc />
public Task<NopReq> UpdateAsync(NopReq req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
@ -129,4 +137,17 @@ public sealed class ApiService(
?.HttpMethods.First()
});
}
private ISelect<Sys_Api> QueryInternal(QueryReq<QueryApiReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
if (!req.Prop?.Equals(nameof(req.Filter.CreatedTime), StringComparison.OrdinalIgnoreCase) ?? true) {
ret = ret.OrderByDescending(a => a.CreatedTime);
}
return ret;
}
}

View File

@ -23,6 +23,7 @@ public sealed class CacheService(IConnectionMultiplexer connectionMultiplexer) /
/// <inheritdoc />
public async Task<PagedQueryRsp<GetAllEntriesRsp>> GetAllEntriesAsync(PagedQueryReq<GetAllEntriesReq> req)
{
req.ThrowIfInvalid();
var database = connectionMultiplexer.GetDatabase((int?)req.Filter?.DbIndex ?? 0);
var redisResults = (RedisResult[])await database
.ExecuteAsync("scan", (req.Page - 1) * req.PageSize, "count"

View File

@ -37,6 +37,7 @@ public sealed class CaptchaService : ServiceBase<ICaptchaService>, ICaptchaServi
/// <inheritdoc />
public Task<bool> VerifyCaptchaAsync(VerifyCaptchaReq req)
{
req.ThrowIfInvalid();
if (req.SawOffsetX == null) {
return Task.FromResult(false);
}

View File

@ -15,6 +15,7 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
/// <inheritdoc />
public async Task<QueryConfigRsp> CreateAsync(CreateConfigReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryConfigRsp>();
}
@ -33,18 +35,21 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryConfigReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryConfigRsp> GetAsync(QueryConfigReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryConfigReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryConfigRsp>();
}
@ -61,6 +66,7 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryConfigRsp>> PagedQueryAsync(PagedQueryReq<QueryConfigReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -74,6 +80,7 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
/// <inheritdoc />
public async Task<IEnumerable<QueryConfigRsp>> QueryAsync(QueryReq<QueryConfigReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryConfigRsp>>();
}
@ -81,6 +88,7 @@ public sealed class ConfigService(DefaultRepository<Sys_Config> rpo) //
/// <inheritdoc />
public async Task<QueryConfigRsp> UpdateAsync(UpdateConfigReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QueryConfigRsp;
}

View File

@ -0,0 +1,9 @@
using NetAdmin.Application.Services;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 计划作业执行记录服务
/// </summary>
public interface IJobRecordService : IService, IJobRecordModule;

View File

@ -0,0 +1,21 @@
using NetAdmin.Application.Services;
using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.SysComponent.Application.Modules.Sys;
namespace NetAdmin.SysComponent.Application.Services.Sys.Dependency;
/// <summary>
/// 计划作业服务
/// </summary>
public interface IJobService : IService, IJobModule
{
/// <summary>
/// 完成计划作业
/// </summary>
Task FinishJobAsync(UpdateJobReq req);
/// <summary>
/// 获取下一个要执行的计划作业
/// </summary>
Task<QueryJobRsp> GetNextJobAsync();
}

View File

@ -14,6 +14,11 @@ public interface IUserService : IService, IUserModule
/// </summary>
Task<QueryUserRsp> GetForUpdateAsync(QueryUserReq req);
/// <summary>
/// 用户编号登录
/// </summary>
Task<LoginRsp> LoginByUserIdAsync(long userId);
/// <summary>
/// 单体更新
/// </summary>

View File

@ -14,6 +14,7 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
/// <exception cref="NetAdminInvalidOperationException">Parent_department_does_not_exist</exception>
public async Task<QueryDeptRsp> CreateAsync(CreateDeptReq req)
{
req.ThrowIfInvalid();
if (req.ParentId != 0 && !await Rpo.Select.AnyAsync(a => a.Id == req.ParentId).ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
@ -40,6 +42,7 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
/// <exception cref="NetAdminInvalidOperationException">该部门下存在子部门</exception>
public async Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
if (await Rpo.Orm.Select<Sys_User>().AnyAsync(a => a.DeptId == req.Id).ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
@ -56,12 +59,14 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryDeptReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryDeptRsp> GetAsync(QueryDeptReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryDeptReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryDeptRsp>();
}
@ -69,12 +74,14 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
/// <inheritdoc />
public Task<PagedQueryRsp<QueryDeptRsp>> PagedQueryAsync(PagedQueryReq<QueryDeptReq> req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public async Task<IEnumerable<QueryDeptRsp>> QueryAsync(QueryReq<QueryDeptReq> req)
{
req.ThrowIfInvalid();
return (await QueryInternal(req).ToTreeListAsync().ConfigureAwait(false)).Adapt<IEnumerable<QueryDeptRsp>>();
}
@ -82,6 +89,7 @@ public sealed class DeptService(DefaultRepository<Sys_Dept> rpo) //
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryDeptRsp> UpdateAsync(UpdateDeptReq req)
{
req.ThrowIfInvalid();
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0
? throw new NetAdminUnexpectedException()
: (await QueryInternal(new QueryReq<QueryDeptReq> { Filter = new QueryDeptReq { Id = req.Id } }, true)

View File

@ -22,6 +22,8 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
/// <inheritdoc />
public async Task GenerateCsCodeAsync(GenerateCsCodeReq req)
{
req.ThrowIfInvalid();
// 模块类型Sys、Biz、等
var moduleType = Enum.GetName(req.Type)!;
@ -111,6 +113,7 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
/// <inheritdoc />
public async Task GenerateIconCodeAsync(GenerateIconCodeReq req)
{
req.ThrowIfInvalid();
var tplSvg = await File.ReadAllTextAsync(
Path.Combine(_clientProjectPath, "src", "assets", "icons", "tpl", "Svg.vue"))
.ConfigureAwait(false);

View File

@ -14,6 +14,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <exception cref="NetAdminInvalidOperationException">The_parent_node_does_not_exist</exception>
public async Task<QueryDicCatalogRsp> CreateAsync(CreateDicCatalogReq req)
{
req.ThrowIfInvalid();
if (req.ParentId != 0 &&
!await Rpo.Where(a => a.Id == req.ParentId).ForUpdate().AnyAsync().ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
@ -38,6 +40,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <inheritdoc />
public async Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.DeleteCascadeByDatabaseAsync(a => a.Id == req.Id).ConfigureAwait(false);
return ret.Count;
}
@ -45,12 +48,14 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryDicCatalogReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryDicCatalogRsp> GetAsync(QueryDicCatalogReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryDicCatalogReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -60,6 +65,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryDicCatalogRsp>> PagedQueryAsync(PagedQueryReq<QueryDicCatalogReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -73,6 +79,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <inheritdoc />
public async Task<IEnumerable<QueryDicCatalogRsp>> QueryAsync(QueryReq<QueryDicCatalogReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).ToTreeListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryDicCatalogRsp>>();
}
@ -82,6 +89,7 @@ public sealed class DicCatalogService(DefaultRepository<Sys_DicCatalog> rpo) //
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryDicCatalogRsp> UpdateAsync(UpdateDicCatalogReq req)
{
req.ThrowIfInvalid();
if (req.ParentId != 0 &&
!await Rpo.Where(a => a.Id == req.ParentId).ForUpdate().AnyAsync().ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);

View File

@ -14,6 +14,7 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
/// <exception cref="NetAdminInvalidOperationException">Dictionary_directory_does_not_exist</exception>
public async Task<QueryDicContentRsp> CreateAsync(CreateDicContentReq req)
{
req.ThrowIfInvalid();
if (!await Rpo.Orm.Select<Sys_DicCatalog>()
.Where(a => a.Id == req.CatalogId)
.ForUpdate()
@ -41,18 +43,21 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryDicContentReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryDicContentRsp> GetAsync(QueryDicContentReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryDicContentReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -62,6 +67,7 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryDicContentRsp>> PagedQueryAsync(PagedQueryReq<QueryDicContentReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -75,6 +81,7 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
/// <inheritdoc />
public async Task<IEnumerable<QueryDicContentRsp>> QueryAsync(QueryReq<QueryDicContentReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryDicContentRsp>>();
}
@ -84,6 +91,7 @@ public sealed class DicContentService(DefaultRepository<Sys_DicContent> rpo) //
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryDicContentRsp> UpdateAsync(UpdateDicContentReq req)
{
req.ThrowIfInvalid();
if (!await Rpo.Orm.Select<Sys_DicCatalog>()
.Where(a => a.Id == req.CatalogId)
.ForUpdate()

View File

@ -13,84 +13,98 @@ public sealed class DicService(IDicCatalogService catalogService, IDicContentSer
/// <inheritdoc />
public Task<int> BulkDeleteCatalogAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
return catalogService.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<int> BulkDeleteContentAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
return contentService.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<QueryDicCatalogRsp> CreateCatalogAsync(CreateDicCatalogReq req)
{
req.ThrowIfInvalid();
return catalogService.CreateAsync(req);
}
/// <inheritdoc />
public Task<QueryDicContentRsp> CreateContentAsync(CreateDicContentReq req)
{
req.ThrowIfInvalid();
return contentService.CreateAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteCatalogAsync(DelReq req)
{
req.ThrowIfInvalid();
return catalogService.DeleteAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteContentAsync(DelReq req)
{
req.ThrowIfInvalid();
return contentService.DeleteAsync(req);
}
/// <inheritdoc />
public Task<QueryDicCatalogRsp> GetCatalogAsync(QueryDicCatalogReq req)
{
req.ThrowIfInvalid();
return catalogService.GetAsync(req);
}
/// <inheritdoc />
public Task<QueryDicContentRsp> GetContentAsync(QueryDicContentReq req)
{
req.ThrowIfInvalid();
return contentService.GetAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryDicCatalogRsp>> PagedQueryCatalogAsync(PagedQueryReq<QueryDicCatalogReq> req)
{
req.ThrowIfInvalid();
return catalogService.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryDicContentRsp>> PagedQueryContentAsync(PagedQueryReq<QueryDicContentReq> req)
{
req.ThrowIfInvalid();
return contentService.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryDicCatalogRsp>> QueryCatalogAsync(QueryReq<QueryDicCatalogReq> req)
{
req.ThrowIfInvalid();
return catalogService.QueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryDicContentRsp>> QueryContentAsync(QueryReq<QueryDicContentReq> req)
{
req.ThrowIfInvalid();
return contentService.QueryAsync(req);
}
/// <inheritdoc />
public Task<QueryDicCatalogRsp> UpdateCatalogAsync(UpdateDicCatalogReq req)
{
req.ThrowIfInvalid();
return catalogService.UpdateAsync(req);
}
/// <inheritdoc />
public Task<QueryDicContentRsp> UpdateContentAsync(UpdateDicContentReq req)
{
req.ThrowIfInvalid();
return contentService.UpdateAsync(req);
}
}

View File

@ -0,0 +1,112 @@
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.JobRecord;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using DataType = FreeSql.DataType;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IJobRecordService" />
public sealed class JobRecordService(DefaultRepository<Sys_JobRecord> rpo) //
: RepositoryService<Sys_JobRecord, IJobRecordService>(rpo), IJobRecordService
{
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
}
return sum;
}
/// <inheritdoc />
public async Task<QueryJobRecordRsp> CreateAsync(CreateJobRecordReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryJobRecordRsp>();
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryJobRecordRsp> GetAsync(QueryJobRecordReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryJobRecordReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
return ret.Adapt<QueryJobRecordRsp>();
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryJobRecordRsp>> PagedQueryAsync(PagedQueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
.ToListAsync()
.ConfigureAwait(false);
return new PagedQueryRsp<QueryJobRecordRsp>(req.Page, req.PageSize, total
, list.Adapt<IEnumerable<QueryJobRecordRsp>>());
}
/// <inheritdoc />
public async Task<IEnumerable<QueryJobRecordRsp>> QueryAsync(QueryReq<QueryJobRecordReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryJobRecordRsp>>();
}
/// <inheritdoc />
public async Task<QueryJobRecordRsp> UpdateAsync(UpdateJobRecordReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QueryJobRecordRsp;
}
var ret = await Rpo.UpdateDiy.SetSource(req).ExecuteUpdatedAsync().ConfigureAwait(false);
return ret.FirstOrDefault()?.Adapt<QueryJobRecordRsp>();
}
/// <inheritdoc />
protected override async Task<Sys_JobRecord> UpdateForSqliteAsync(Sys_JobRecord req)
{
return await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0
? null
: await GetAsync(new QueryJobRecordReq { Id = req.Id }).ConfigureAwait(false);
}
private ISelect<Sys_JobRecord> QueryInternal(QueryReq<QueryJobRecordReq> req)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.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);
}
return ret;
}
}

View File

@ -0,0 +1,175 @@
using Cronos;
using NetAdmin.Application.Repositories;
using NetAdmin.Application.Services;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.Domain.Enums.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using DataType = FreeSql.DataType;
namespace NetAdmin.SysComponent.Application.Services.Sys;
/// <inheritdoc cref="IJobService" />
public sealed class JobService(DefaultRepository<Sys_Job> rpo) //
: RepositoryService<Sys_Job, IJobService>(rpo), IJobService
{
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
}
return sum;
}
/// <inheritdoc />
public async Task<QueryJobRsp> CreateAsync(CreateJobReq req)
{
req.ThrowIfInvalid();
var nextExecTime = CronExpression.Parse(req.ExecutionCron).GetNextOccurrence(DateTime.UtcNow, TimeZoneInfo.Utc);
var ret = await Rpo.InsertAsync(req with {
NextExecTime = nextExecTime
, NextTimeId = nextExecTime?.TimeUnixUtc()
, RequestHeader = req.RequestHeaders?.Json()
})
.ConfigureAwait(false);
return ret.Adapt<QueryJobRsp>();
}
/// <inheritdoc />
public async Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.DeleteCascadeByDatabaseAsync(a => a.Id == req.Id).ConfigureAwait(false);
return ret.Count;
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryJobReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task FinishJobAsync(UpdateJobReq req)
{
var nextExecTime = CronExpression.Parse(req.ExecutionCron).GetNextOccurrence(DateTime.UtcNow, TimeZoneInfo.Utc);
_ = await UpdateAsync(req with {
Status = JobStatues.Idle
, NextExecTime = nextExecTime
, NextTimeId = nextExecTime?.TimeUnixUtc()
})
.ConfigureAwait(false);
}
/// <inheritdoc />
public async Task<QueryJobRsp> GetAsync(QueryJobReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryJobReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryJobRsp>();
}
/// <inheritdoc />
public async Task<QueryJobRsp> GetNextJobAsync()
{
var df = new DynamicFilterInfo {
Filters = [
new DynamicFilterInfo {
Field = nameof(QueryJobReq.NextExecTime)
, Value = DateTime.UtcNow
, Operator = DynamicFilterOperators.LessThan
}
, new DynamicFilterInfo {
Field = nameof(QueryJobReq.Status)
, Value = JobStatues.Idle
, Operator = DynamicFilterOperators.Eq
}
, new DynamicFilterInfo {
Field = nameof(QueryJobReq.Enabled)
, Value = true
, Operator = DynamicFilterOperators.Eq
}
]
};
var job = await QueryInternal(new QueryReq<QueryJobReq> { DynamicFilter = df, Count = 1 }, true)
.Where(a => !Rpo.Orm.Select<Sys_JobRecord>()
.As("b")
.Where(b => b.JobId == a.Id && b.TimeId == a.NextTimeId)
.Any())
.ToOneAsync()
.ConfigureAwait(false);
return job == null
? null
: await UpdateAsync(job.Adapt<UpdateJobReq>() with {
Status = JobStatues.Running
, LastExecTime = DateTime.UtcNow
})
.ConfigureAwait(false);
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryJobRsp>> PagedQueryAsync(PagedQueryReq<QueryJobReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
.ToListAsync()
.ConfigureAwait(false);
return new PagedQueryRsp<QueryJobRsp>(req.Page, req.PageSize, total, list.Adapt<IEnumerable<QueryJobRsp>>());
}
/// <inheritdoc />
public async Task<IEnumerable<QueryJobRsp>> QueryAsync(QueryReq<QueryJobReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryJobRsp>>();
}
/// <inheritdoc />
public Task SetEnabledAsync(UpdateJobReq req)
{
req.ThrowIfInvalid();
return Rpo.UpdateDiy.Set(a => a.Enabled == req.Enabled).Where(a => a.Id == req.Id).ExecuteAffrowsAsync();
}
/// <inheritdoc />
public async Task<QueryJobRsp> UpdateAsync(UpdateJobReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return (await UpdateForSqliteAsync(req).ConfigureAwait(false)).Adapt<QueryJobRsp>();
}
_ = await Rpo.UpdateAsync(req).ConfigureAwait(false);
return req.Adapt<QueryJobRsp>();
}
/// <inheritdoc />
protected override async Task<Sys_Job> UpdateForSqliteAsync(Sys_Job req)
{
_ = await Rpo.UpdateAsync(req).ConfigureAwait(false);
return req;
}
private ISelect<Sys_Job> QueryInternal(QueryReq<QueryJobReq> req, bool orderByRandom = false)
{
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
.WhereDynamic(req.Filter)
.WhereIf( //
req.Keywords?.Length > 0
, a => a.Id == req.Keywords.Int64Try(0) || a.JobName.Contains(req.Keywords))
.OrderByPropertyNameIf(req.Prop?.Length > 0, req.Prop, req.Order == Orders.Ascending);
return !orderByRandom && (!req.Prop?.Equals(nameof(req.Filter.Id), StringComparison.OrdinalIgnoreCase) ?? true)
? ret.OrderByDescending(a => a.Id)
: ret.OrderByRandom();
}
}

View File

@ -14,6 +14,7 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -25,6 +26,7 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
/// <inheritdoc />
public async Task<QueryMenuRsp> CreateAsync(CreateMenuReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryMenuRsp>();
}
@ -32,18 +34,21 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryMenuReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryMenuRsp> GetAsync(QueryMenuReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryMenuReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryMenuRsp>();
}
@ -51,12 +56,14 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
/// <inheritdoc />
public Task<PagedQueryRsp<QueryMenuRsp>> PagedQueryAsync(PagedQueryReq<QueryMenuReq> req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public async Task<IEnumerable<QueryMenuRsp>> QueryAsync(QueryReq<QueryMenuReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).ToTreeListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryMenuRsp>>();
}
@ -65,6 +72,7 @@ public sealed class MenuService(DefaultRepository<Sys_Menu> rpo, IUserService us
/// <exception cref="NetAdminUnexpectedException">NetAdminUnexpectedException</exception>
public async Task<QueryMenuRsp> UpdateAsync(UpdateMenuReq req)
{
req.ThrowIfInvalid();
if (await Rpo.UpdateDiy.SetSource(req).ExecuteAffrowsAsync().ConfigureAwait(false) <= 0) {
throw new NetAdminUnexpectedException();
}

View File

@ -14,6 +14,7 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -25,6 +26,7 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
/// <inheritdoc />
public async Task<QueryRequestLogRsp> CreateAsync(CreateRequestLogReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryRequestLogRsp>();
}
@ -32,18 +34,21 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryRequestLogRsp> GetAsync(QueryRequestLogReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRequestLogReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -53,6 +58,7 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryRequestLogRsp>> PagedQueryAsync(PagedQueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -78,6 +84,7 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
/// <inheritdoc />
public async Task<IEnumerable<QueryRequestLogRsp>> QueryAsync(QueryReq<QueryRequestLogReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryRequestLogRsp>>();
}
@ -85,6 +92,7 @@ public sealed class RequestLogService(DefaultRepository<Sys_RequestLog> rpo) //
/// <inheritdoc />
public Task<NopReq> UpdateAsync(NopReq req)
{
req.ThrowIfInvalid();
throw new NotImplementedException();
}

View File

@ -14,6 +14,7 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -25,6 +26,7 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <inheritdoc />
public async Task<QueryRoleRsp> CreateAsync(CreateRoleReq req)
{
req.ThrowIfInvalid();
var entity = req.Adapt<Sys_Role>();
var ret = await Rpo.InsertAsync(entity).ConfigureAwait(false);
@ -40,6 +42,7 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <exception cref="NetAdminInvalidOperationException">Users_exist_under_this_role_and_deletion_is_not_allowed</exception>
public async Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return await Rpo.Orm.Select<Sys_UserRole>().ForUpdate().AnyAsync(a => a.RoleId == req.Id).ConfigureAwait(false)
? throw new NetAdminInvalidOperationException(Ln.)
: await Rpo.DeleteAsync(a => a.Id == req.Id).ConfigureAwait(false);
@ -48,12 +51,14 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryRoleReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryRoleRsp> GetAsync(QueryRoleReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryRoleReq> { Filter = req }).ToOneAsync().ConfigureAwait(false);
return ret.Adapt<QueryRoleRsp>();
}
@ -61,6 +66,7 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryRoleRsp>> PagedQueryAsync(PagedQueryReq<QueryRoleReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -73,6 +79,7 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <inheritdoc />
public async Task<IEnumerable<QueryRoleRsp>> QueryAsync(QueryReq<QueryRoleReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryRoleRsp>>();
}
@ -80,6 +87,7 @@ public sealed class RoleService(DefaultRepository<Sys_Role> rpo) //
/// <inheritdoc />
public async Task<QueryRoleRsp> UpdateAsync(UpdateRoleReq req)
{
req.ThrowIfInvalid();
var entity = req.Adapt<Sys_Role>();
_ = await Rpo.UpdateAsync(entity).ConfigureAwait(false);
await Rpo.SaveManyAsync(entity, nameof(entity.Depts)).ConfigureAwait(false);

View File

@ -15,6 +15,7 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgDeptRsp> CreateAsync(CreateSiteMsgDeptReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgDeptRsp>();
}
@ -33,18 +35,21 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QuerySiteMsgDeptReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QuerySiteMsgDeptRsp> GetAsync(QuerySiteMsgDeptReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgDeptReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -54,6 +59,7 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
/// <inheritdoc />
public async Task<PagedQueryRsp<QuerySiteMsgDeptRsp>> PagedQueryAsync(PagedQueryReq<QuerySiteMsgDeptReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -67,6 +73,7 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
/// <inheritdoc />
public async Task<IEnumerable<QuerySiteMsgDeptRsp>> QueryAsync(QueryReq<QuerySiteMsgDeptReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QuerySiteMsgDeptRsp>>();
}
@ -74,6 +81,7 @@ public sealed class SiteMsgDeptService(DefaultRepository<Sys_SiteMsgDept> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgDeptRsp> UpdateAsync(UpdateSiteMsgDeptReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QuerySiteMsgDeptRsp;
}

View File

@ -15,6 +15,7 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgFlagRsp> CreateAsync(CreateSiteMsgFlagReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgFlagRsp>();
}
@ -33,18 +35,21 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QuerySiteMsgFlagReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QuerySiteMsgFlagRsp> GetAsync(QuerySiteMsgFlagReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgFlagReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -54,6 +59,7 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
/// <inheritdoc />
public async Task<PagedQueryRsp<QuerySiteMsgFlagRsp>> PagedQueryAsync(PagedQueryReq<QuerySiteMsgFlagReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -67,6 +73,7 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
/// <inheritdoc />
public async Task<IEnumerable<QuerySiteMsgFlagRsp>> QueryAsync(QueryReq<QuerySiteMsgFlagReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QuerySiteMsgFlagRsp>>();
}
@ -74,6 +81,7 @@ public sealed class SiteMsgFlagService(DefaultRepository<Sys_SiteMsgFlag> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgFlagRsp> UpdateAsync(UpdateSiteMsgFlagReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QuerySiteMsgFlagRsp;
}

View File

@ -15,6 +15,7 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgRoleRsp> CreateAsync(CreateSiteMsgRoleReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgRoleRsp>();
}
@ -33,18 +35,21 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QuerySiteMsgRoleReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QuerySiteMsgRoleRsp> GetAsync(QuerySiteMsgRoleReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgRoleReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -54,6 +59,7 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
/// <inheritdoc />
public async Task<PagedQueryRsp<QuerySiteMsgRoleRsp>> PagedQueryAsync(PagedQueryReq<QuerySiteMsgRoleReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -67,6 +73,7 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
/// <inheritdoc />
public async Task<IEnumerable<QuerySiteMsgRoleRsp>> QueryAsync(QueryReq<QuerySiteMsgRoleReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QuerySiteMsgRoleRsp>>();
}
@ -74,6 +81,7 @@ public sealed class SiteMsgRoleService(DefaultRepository<Sys_SiteMsgRole> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgRoleRsp> UpdateAsync(UpdateSiteMsgRoleReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QuerySiteMsgRoleRsp;
}

View File

@ -21,6 +21,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -32,6 +33,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task<QuerySiteMsgRsp> CreateAsync(CreateSiteMsgReq req)
{
req.ThrowIfInvalid();
await CreateUpdateCheckAsync(req).ConfigureAwait(false);
// 主表
@ -56,6 +58,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.DeleteCascadeByDatabaseAsync(a => a.Id == req.Id).ConfigureAwait(false);
return ret.Count;
}
@ -63,12 +66,14 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QuerySiteMsgReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QuerySiteMsgRsp> GetAsync(QuerySiteMsgReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgReq> { Filter = req })
.IncludeMany(a => a.Roles)
.IncludeMany(a => a.Users)
@ -81,6 +86,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task<QuerySiteMsgRsp> GetMineAsync(QuerySiteMsgReq req)
{
req.ThrowIfInvalid();
var ret = await PagedQueryMineAsync(
new PagedQueryReq<QuerySiteMsgReq> {
DynamicFilter
@ -97,6 +103,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task<PagedQueryRsp<QuerySiteMsgRsp>> PagedQueryAsync(PagedQueryReq<QuerySiteMsgReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -118,12 +125,14 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public Task<PagedQueryRsp<QuerySiteMsgRsp>> PagedQueryMineAsync(PagedQueryReq<QuerySiteMsgReq> req)
{
req.ThrowIfInvalid();
return PagedQueryMineAsync(req, false);
}
/// <inheritdoc />
public async Task<IEnumerable<QuerySiteMsgRsp>> QueryAsync(QueryReq<QuerySiteMsgReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QuerySiteMsgRsp>>();
}
@ -131,6 +140,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task SetSiteMsgStatusAsync(UpdateSiteMsgFlagReq req)
{
req.ThrowIfInvalid();
if (!await ExistAsync(new QueryReq<QuerySiteMsgReq> { Filter = new QuerySiteMsgReq { Id = req.SiteMsgId } })
.ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
@ -160,6 +170,7 @@ public sealed class SiteMsgService(
/// <inheritdoc />
public async Task<QuerySiteMsgRsp> UpdateAsync(UpdateSiteMsgReq req)
{
req.ThrowIfInvalid();
await CreateUpdateCheckAsync(req).ConfigureAwait(false);
// 主表

View File

@ -15,6 +15,7 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgUserRsp> CreateAsync(CreateSiteMsgUserReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QuerySiteMsgUserRsp>();
}
@ -33,18 +35,21 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QuerySiteMsgUserReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QuerySiteMsgUserRsp> GetAsync(QuerySiteMsgUserReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QuerySiteMsgUserReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -54,6 +59,7 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
/// <inheritdoc />
public async Task<PagedQueryRsp<QuerySiteMsgUserRsp>> PagedQueryAsync(PagedQueryReq<QuerySiteMsgUserReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -67,6 +73,7 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
/// <inheritdoc />
public async Task<IEnumerable<QuerySiteMsgUserRsp>> QueryAsync(QueryReq<QuerySiteMsgUserReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QuerySiteMsgUserRsp>>();
}
@ -74,6 +81,7 @@ public sealed class SiteMsgUserService(DefaultRepository<Sys_SiteMsgUser> rpo) /
/// <inheritdoc />
public async Task<QuerySiteMsgUserRsp> UpdateAsync(UpdateSiteMsgUserReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QuerySiteMsgUserRsp;
}

View File

@ -16,6 +16,7 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -27,6 +28,7 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
/// <inheritdoc />
public async Task<QueryUserProfileRsp> CreateAsync(CreateUserProfileReq req)
{
req.ThrowIfInvalid();
var entity = req.Adapt<Sys_UserProfile>();
var ret = await Rpo.InsertAsync(entity).ConfigureAwait(false);
return ret.Adapt<QueryUserProfileRsp>();
@ -35,18 +37,21 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryUserProfileReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryUserProfileRsp> GetAsync(QueryUserProfileReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryUserProfileReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -56,6 +61,7 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryUserProfileRsp>> PagedQueryAsync(PagedQueryReq<QueryUserProfileReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -82,6 +88,7 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
/// <inheritdoc />
public async Task<IEnumerable<QueryUserProfileRsp>> QueryAsync(QueryReq<QueryUserProfileReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req)
.Take(req.Count)
.ToListAsync((a, b, c, d, e) => new {
@ -115,6 +122,7 @@ public sealed class UserProfileService(DefaultRepository<Sys_UserProfile> rpo) /
/// <inheritdoc />
public async Task<QueryUserProfileRsp> UpdateAsync(UpdateUserProfileReq req)
{
req.ThrowIfInvalid();
var entity = req.Adapt<Sys_UserProfile>();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(entity).ConfigureAwait(false) as QueryUserProfileRsp;

View File

@ -38,6 +38,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -49,12 +50,14 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<bool> CheckMobileAvailableAsync(CheckMobileAvailableReq req)
{
req.ThrowIfInvalid();
return !await Rpo.Select.Where(a => a.Mobile == req.Mobile && a.Id != req.Id).AnyAsync().ConfigureAwait(false);
}
/// <inheritdoc />
public async Task<bool> CheckUserNameAvailableAsync(CheckUserNameAvailableReq req)
{
req.ThrowIfInvalid();
return !await Rpo.Select.Where(a => a.UserName == req.UserName && a.Id != req.Id)
.AnyAsync()
.ConfigureAwait(false);
@ -63,6 +66,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<QueryUserRsp> CreateAsync(CreateUserReq req)
{
req.ThrowIfInvalid();
await CreateUpdateCheckAsync(req).ConfigureAwait(false);
// 主表
@ -82,6 +86,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
var effect = 0;
// 删除主表
@ -101,12 +106,14 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<bool> ExistAsync(QueryReq<QueryUserReq> req)
{
req.ThrowIfInvalid();
return await (await QueryInternalAsync(req).ConfigureAwait(false)).AnyAsync().ConfigureAwait(false);
}
/// <inheritdoc />
public async Task<QueryUserRsp> GetAsync(QueryUserReq req)
{
req.ThrowIfInvalid();
var ret = await (await QueryInternalAsync(new QueryReq<QueryUserReq> { Filter = req }).ConfigureAwait(false))
.ToOneAsync()
.ConfigureAwait(false);
@ -116,6 +123,8 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<QueryUserRsp> GetForUpdateAsync(QueryUserReq req)
{
req.ThrowIfInvalid();
// ReSharper disable once MethodHasAsyncOverload
#pragma warning disable VSTHRD103
return (await QueryInternal(new QueryReq<QueryUserReq> { Filter = req })
@ -129,6 +138,7 @@ public sealed class UserService(
/// <exception cref="NetAdminInvalidOperationException">用户名或密码错误</exception>
public async Task<LoginRsp> LoginByPwdAsync(LoginByPwdReq req)
{
req.ThrowIfInvalid();
var pwd = req.Password.Pwd().Guid();
Sys_User dbUser;
@ -155,6 +165,7 @@ public sealed class UserService(
/// <exception cref="NetAdminInvalidOperationException">用户不存在</exception>
public async Task<LoginRsp> LoginBySmsAsync(LoginBySmsReq req)
{
req.ThrowIfInvalid();
if (!await verifyCodeService.VerifyAsync(req.Adapt<VerifySmsCodeReq>()).ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
@ -163,9 +174,18 @@ public sealed class UserService(
return dbUser == null ? throw new NetAdminInvalidOperationException(Ln.) : LoginInternal(dbUser);
}
/// <inheritdoc />
public async Task<LoginRsp> LoginByUserIdAsync(long userId)
{
var dbUser = await Rpo.Where(a => a.Id == userId).ToOneAsync().ConfigureAwait(false);
return LoginInternal(dbUser);
}
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryUserRsp>> PagedQueryAsync(PagedQueryReq<QueryUserReq> req)
{
req.ThrowIfInvalid();
var list = await (await QueryInternalAsync(req).ConfigureAwait(false)).Page(req.Page, req.PageSize)
.Count(out var total)
.ToListAsync(_selectUserFields)
@ -176,6 +196,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<IEnumerable<QueryUserRsp>> QueryAsync(QueryReq<QueryUserReq> req)
{
req.ThrowIfInvalid();
var list = await (await QueryInternalAsync(req).ConfigureAwait(false)).Take(req.Count)
.ToListAsync(_selectUserFields)
.ConfigureAwait(false);
@ -185,6 +206,7 @@ public sealed class UserService(
/// <inheritdoc />
public Task<IEnumerable<QueryUserProfileRsp>> QueryProfileAsync(QueryReq<QueryUserProfileReq> req)
{
req.ThrowIfInvalid();
return userProfileService.QueryAsync(req);
}
@ -192,6 +214,7 @@ public sealed class UserService(
/// <exception cref="NetAdminInvalidOperationException">验证码不正确</exception>
public async Task<UserInfoRsp> RegisterAsync(RegisterUserReq req)
{
req.ThrowIfInvalid();
if (!await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
@ -205,6 +228,7 @@ public sealed class UserService(
/// <exception cref="NetAdminInvalidOperationException">用户不存在</exception>
public async Task<uint> ResetPasswordAsync(ResetPasswordReq req)
{
req.ThrowIfInvalid();
return !await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)
? throw new NetAdminInvalidOperationException(Ln.)
: (uint)await Rpo.UpdateDiy
@ -221,6 +245,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<UserInfoRsp> SetAvatarAsync(SetAvatarReq req)
{
req.ThrowIfInvalid();
if (await Rpo.UpdateDiy
.SetSource(req with {
Id = UserToken.Id
@ -244,6 +269,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<UserInfoRsp> SetEmailAsync(SetEmailReq req)
{
req.ThrowIfInvalid();
var user = Rpo.Where(a => a.Id == UserToken.Id).ToOne(a => new { a.Mobile, a.Version, a.Email });
// 如果已绑定手机号、需要手机安全验证
@ -277,6 +303,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<UserInfoRsp> SetMobileAsync(SetMobileReq req)
{
req.ThrowIfInvalid();
var user = await Rpo.Where(a => a.Id == UserToken.Id)
.ToOneAsync(a => new { a.Version, a.Mobile })
.ConfigureAwait(false);
@ -321,6 +348,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<uint> SetPasswordAsync(SetPasswordReq req)
{
req.ThrowIfInvalid();
var version = await Rpo.Where(a => a.Id == UserToken.Id && a.Password == req.OldPassword.Pwd().Guid())
.ToOneAsync(a => new long?(a.Version))
.ConfigureAwait(false);
@ -343,6 +371,7 @@ public sealed class UserService(
/// <inheritdoc />
public async Task<QueryUserRsp> UpdateAsync(UpdateUserReq req)
{
req.ThrowIfInvalid();
await CreateUpdateCheckAsync(req).ConfigureAwait(false);
// 主表
@ -374,6 +403,7 @@ public sealed class UserService(
/// <inheritdoc />
public Task UpdateSingleAsync(UpdateUserReq req)
{
req.ThrowIfInvalid();
return Rpo.UpdateAsync(req);
}

View File

@ -19,6 +19,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -30,6 +31,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<QueryVerifyCodeRsp> CreateAsync(CreateVerifyCodeReq req)
{
req.ThrowIfInvalid();
var entity = await Rpo.InsertAsync(req).ConfigureAwait(false);
var ret = entity.Adapt<QueryVerifyCodeRsp>();
@ -43,18 +45,21 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryVerifyCodeReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryVerifyCodeRsp> GetAsync(QueryVerifyCodeReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryVerifyCodeReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -64,6 +69,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryVerifyCodeRsp>> PagedQueryAsync(PagedQueryReq<QueryVerifyCodeReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -77,6 +83,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<IEnumerable<QueryVerifyCodeRsp>> QueryAsync(QueryReq<QueryVerifyCodeReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryVerifyCodeRsp>>();
}
@ -84,6 +91,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<SendVerifyCodeRsp> SendVerifyCodeAsync(SendVerifyCodeReq req)
{
req.ThrowIfInvalid();
var lastSent = await GetLastSentAsync(req.DestDevice).ConfigureAwait(false);
QueryVerifyCodeRsp ret;
@ -110,6 +118,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<QueryVerifyCodeRsp> UpdateAsync(UpdateVerifyCodeReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QueryVerifyCodeRsp;
}
@ -121,6 +130,7 @@ public sealed class VerifyCodeService(DefaultRepository<Sys_VerifyCode> rpo, IEv
/// <inheritdoc />
public async Task<bool> VerifyAsync(VerifyCodeReq req)
{
req.ThrowIfInvalid();
#if DEBUG
if (req.Code == "8888") {
return true;

View File

@ -15,6 +15,7 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
/// <inheritdoc />
public async Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
req.ThrowIfInvalid();
var sum = 0;
foreach (var item in req.Items) {
sum += await DeleteAsync(item).ConfigureAwait(false);
@ -26,6 +27,7 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
/// <inheritdoc />
public async Task<QueryExampleRsp> CreateAsync(CreateExampleReq req)
{
req.ThrowIfInvalid();
var ret = await Rpo.InsertAsync(req).ConfigureAwait(false);
return ret.Adapt<QueryExampleRsp>();
}
@ -33,18 +35,21 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
req.ThrowIfInvalid();
return Rpo.DeleteAsync(a => a.Id == req.Id);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryExampleReq> req)
{
req.ThrowIfInvalid();
return QueryInternal(req).AnyAsync();
}
/// <inheritdoc />
public async Task<QueryExampleRsp> GetAsync(QueryExampleReq req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(new QueryReq<QueryExampleReq> { Filter = req })
.ToOneAsync()
.ConfigureAwait(false);
@ -54,6 +59,7 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
/// <inheritdoc />
public async Task<PagedQueryRsp<QueryExampleRsp>> PagedQueryAsync(PagedQueryReq<QueryExampleReq> req)
{
req.ThrowIfInvalid();
var list = await QueryInternal(req)
.Page(req.Page, req.PageSize)
.Count(out var total)
@ -67,6 +73,7 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
/// <inheritdoc />
public async Task<IEnumerable<QueryExampleRsp>> QueryAsync(QueryReq<QueryExampleReq> req)
{
req.ThrowIfInvalid();
var ret = await QueryInternal(req).Take(req.Count).ToListAsync().ConfigureAwait(false);
return ret.Adapt<IEnumerable<QueryExampleRsp>>();
}
@ -74,6 +81,7 @@ public sealed class ExampleService(DefaultRepository<Tpl_Example> rpo) //
/// <inheritdoc />
public async Task<QueryExampleRsp> UpdateAsync(UpdateExampleReq req)
{
req.ThrowIfInvalid();
if (Rpo.Orm.Ado.DataType == DataType.Sqlite) {
return await UpdateForSqliteAsync(req).ConfigureAwait(false) as QueryExampleRsp;
}

View File

@ -0,0 +1,10 @@
using NetAdmin.Cache;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// <summary>
/// 计划作业缓存
/// </summary>
public interface IJobCache : ICache<IDistributedCache, IJobService>, IJobModule;

View File

@ -0,0 +1,10 @@
using NetAdmin.Cache;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// <summary>
/// 计划作业执行记录缓存
/// </summary>
public interface IJobRecordCache : ICache<IDistributedCache, IJobRecordService>, IJobRecordModule;

View File

@ -0,0 +1,66 @@
using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="IJobCache" />
public sealed class JobCache(IDistributedCache cache, IJobService service)
: DistributedCache<IJobService>(cache, service), IScoped, IJobCache
{
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Service.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<QueryJobRsp> CreateAsync(CreateJobReq req)
{
return Service.CreateAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
return Service.DeleteAsync(req);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryJobReq> req)
{
return Service.ExistAsync(req);
}
/// <inheritdoc />
public Task<QueryJobRsp> GetAsync(QueryJobReq req)
{
return Service.GetAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryJobRsp>> PagedQueryAsync(PagedQueryReq<QueryJobReq> req)
{
return Service.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryJobRsp>> QueryAsync(QueryReq<QueryJobReq> req)
{
return Service.QueryAsync(req);
}
/// <inheritdoc />
public Task SetEnabledAsync(UpdateJobReq req)
{
return Service.SetEnabledAsync(req);
}
/// <inheritdoc />
public Task<QueryJobRsp> UpdateAsync(UpdateJobReq req)
{
return Service.UpdateAsync(req);
}
}

View File

@ -0,0 +1,60 @@
using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.JobRecord;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Cache.Sys;
/// <inheritdoc cref="IJobRecordCache" />
public sealed class JobRecordCache(IDistributedCache cache, IJobRecordService service)
: DistributedCache<IJobRecordService>(cache, service), IScoped, IJobRecordCache
{
/// <inheritdoc />
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Service.BulkDeleteAsync(req);
}
/// <inheritdoc />
public Task<QueryJobRecordRsp> CreateAsync(CreateJobRecordReq req)
{
return Service.CreateAsync(req);
}
/// <inheritdoc />
public Task<int> DeleteAsync(DelReq req)
{
return Service.DeleteAsync(req);
}
/// <inheritdoc />
public Task<bool> ExistAsync(QueryReq<QueryJobRecordReq> req)
{
return Service.ExistAsync(req);
}
/// <inheritdoc />
public Task<QueryJobRecordRsp> GetAsync(QueryJobRecordReq req)
{
return Service.GetAsync(req);
}
/// <inheritdoc />
public Task<PagedQueryRsp<QueryJobRecordRsp>> PagedQueryAsync(PagedQueryReq<QueryJobRecordReq> req)
{
return Service.PagedQueryAsync(req);
}
/// <inheritdoc />
public Task<IEnumerable<QueryJobRecordRsp>> QueryAsync(QueryReq<QueryJobRecordReq> req)
{
return Service.QueryAsync(req);
}
/// <inheritdoc />
public Task<QueryJobRecordRsp> UpdateAsync(UpdateJobRecordReq req)
{
return Service.UpdateAsync(req);
}
}

View File

@ -0,0 +1,92 @@
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.Host.Attributes;
using NetAdmin.Host.Controllers;
using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
using NetAdmin.SysComponent.Cache.Sys.Dependency;
namespace NetAdmin.SysComponent.Host.Controllers.Sys;
/// <summary>
/// 计划作业服务
/// </summary>
[ApiDescriptionSettings(nameof(Sys), Module = nameof(Sys))]
public sealed class JobController(IJobCache cache) : ControllerBase<IJobCache, IJobService>(cache), IJobModule
{
/// <summary>
/// 批量删除计划作业
/// </summary>
[Transaction]
public Task<int> BulkDeleteAsync(BulkReq<DelReq> req)
{
return Cache.BulkDeleteAsync(req);
}
/// <summary>
/// 创建计划作业
/// </summary>
[Transaction]
public Task<QueryJobRsp> CreateAsync(CreateJobReq req)
{
return Cache.CreateAsync(req);
}
/// <summary>
/// 删除计划作业
/// </summary>
[Transaction]
public Task<int> DeleteAsync(DelReq req)
{
return Cache.DeleteAsync(req);
}
/// <summary>
/// 计划作业是否存在
/// </summary>
public Task<bool> ExistAsync(QueryReq<QueryJobReq> req)
{
return Cache.ExistAsync(req);
}
/// <summary>
/// 获取单个计划作业
/// </summary>
public Task<QueryJobRsp> GetAsync(QueryJobReq req)
{
return Cache.GetAsync(req);
}
/// <summary>
/// 分页查询计划作业
/// </summary>
public Task<PagedQueryRsp<QueryJobRsp>> PagedQueryAsync(PagedQueryReq<QueryJobReq> req)
{
return Cache.PagedQueryAsync(req);
}
/// <summary>
/// 查询计划作业
/// </summary>
public Task<IEnumerable<QueryJobRsp>> QueryAsync(QueryReq<QueryJobReq> req)
{
return Cache.QueryAsync(req);
}
/// <summary>
/// 启用/禁用作业
/// </summary>
public Task SetEnabledAsync(UpdateJobReq req)
{
return Cache.SetEnabledAsync(req);
}
/// <summary>
/// 更新计划作业
/// </summary>
[Transaction]
public Task<QueryJobRsp> UpdateAsync(UpdateJobReq req)
{
return Cache.UpdateAsync(req);
}
}

View File

@ -0,0 +1,21 @@
using Furion.Schedule;
using NetAdmin.SysComponent.Host.Jobs;
namespace NetAdmin.SysComponent.Host.Extensions;
/// <summary>
/// ServiceCollection 扩展方法
/// </summary>
[SuppressSniffer]
public static class ServiceCollectionExtensions
{
/// <summary>
/// 添加定时任务
/// </summary>
public static IServiceCollection AddSchedules(this IServiceCollection me)
{
return me.AddSchedule( //
builder => builder //
.AddJob<ScheduledJob>(false, Triggers.PeriodSeconds(5).SetRunOnStart(true)));
}
}

View File

@ -0,0 +1,141 @@
using FreeSql.Internal;
using Furion.RemoteRequest;
using Furion.RemoteRequest.Extensions;
using Furion.Schedule;
using NetAdmin.Domain.DbMaps.Sys;
using NetAdmin.Domain.Dto.Sys.Job;
using NetAdmin.Domain.Dto.Sys.JobRecord;
using NetAdmin.Host.BackgroundRunning;
using NetAdmin.Host.Extensions;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Host.Jobs;
/// <summary>
/// 计划作业
/// </summary>
public sealed class ScheduledJob : WorkBase<ScheduledJob>, IJob
{
private static string _accessToken;
private static string _refreshToken;
private readonly IJobRecordService _jobRecordService;
private readonly IJobService _jobService;
private readonly ILogger<ScheduledJob> _logger;
private readonly IUserService _userService;
private string _requestHeader;
/// <summary>
/// Initializes a new instance of the <see cref="ScheduledJob" /> class.
/// </summary>
public ScheduledJob()
{
_jobRecordService = ServiceProvider.GetService<IJobRecordService>();
_jobService = ServiceProvider.GetService<IJobService>();
_logger = ServiceProvider.GetService<ILogger<ScheduledJob>>();
_userService = ServiceProvider.GetService<IUserService>();
}
/// <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(stoppingToken).ConfigureAwait(false);
}
/// <summary>
/// 通用工作流
/// </summary>
/// <exception cref="NotImplementedException">NotImplementedException</exception>
/// <exception cref="ArgumentOutOfRangeException">ArgumentOutOfRangeException</exception>
protected override async ValueTask WorkflowAsync(CancellationToken cancelToken)
{
QueryJobRsp job = null;
try {
job = await _jobService.GetNextJobAsync().ConfigureAwait(false);
}
catch (DbUpdateVersionException) {
// ignore
}
if (job == null) {
_logger.Info(Ln.);
return;
}
var request = BuildRequest(job);
var sw = new Stopwatch();
sw.Start();
var rsp = await request.SendAsync(cancelToken).ConfigureAwait(false);
if (rsp.StatusCode == HttpStatusCode.Unauthorized) {
var loginRsp = await _userService.LoginByUserIdAsync(job.UserId).ConfigureAwait(false);
#pragma warning disable S2696
_accessToken = loginRsp.AccessToken;
_refreshToken = loginRsp.RefreshToken;
#pragma warning restore S2696
request = BuildRequest(job);
rsp = await request.SendAsync(cancelToken).ConfigureAwait(false);
}
sw.Stop();
await UowManager.AtomicOperateAsync(async () => {
var rspBody = await rsp.Content.ReadAsStringAsync(cancelToken).ConfigureAwait(false);
var jobRecord = new CreateJobRecordReq //
{
Duration = sw.ElapsedMilliseconds
, HttpMethod = job.HttpMethod
, HttpStatusCode = rsp.StatusCode
, JobId = job.Id
, RequestBody = job.RequestBody
, RequestHeader = _requestHeader
, RequestUrl = job.RequestUrl
, ResponseBody = rspBody
, ResponseHeader = rsp.Headers.Json()
, TimeId = job.NextTimeId!.Value
};
_ = await _jobRecordService.CreateAsync(jobRecord).ConfigureAwait(false);
await _jobService
.FinishJobAsync(job.Adapt<UpdateJobReq>() with { LastStatusCode = rsp.StatusCode })
.ConfigureAwait(false);
})
.ConfigureAwait(false);
}
private HttpRequestPart BuildRequest(Sys_Job job)
{
var ret = job.RequestUrl.SetHttpMethod(new HttpMethod(job.HttpMethod.ToString()));
var headers = new Dictionary<string, string>();
if (!_accessToken.NullOrEmpty()) {
headers.Add(Chars.FLG_ACCESS_TOKEN_HEADER_KEY, $"{Chars.FLG_AUTH_SCHEMA} {_accessToken}");
}
if (!_refreshToken.NullOrEmpty()) {
headers.Add(Chars.FLG_X_ACCESS_TOKEN_HEADER_KEY, $"{Chars.FLG_AUTH_SCHEMA} {_refreshToken}");
}
if (!job.RequestHeader.NullOrEmpty()) {
ret = ret.SetHeaders(headers.Union(job.RequestHeader.Object<Dictionary<string, string>>())
.ToDictionary(x => x.Key, x => x.Value));
}
if (!job.RequestBody.NullOrEmpty()) {
ret = ret.SetBody(job.RequestBody);
}
return ret.OnResponsing(GetRequestHeader).OnException(GetRequestHeader);
}
private void GetRequestHeader(HttpClient _, HttpResponseMessage rsp, string __)
{
_requestHeader = rsp!.RequestMessage!.Headers.Json();
}
private void GetRequestHeader(HttpClient _, HttpResponseMessage rsp)
{
GetRequestHeader(_, rsp, null);
}
}

View File

@ -0,0 +1,106 @@
/**
* 计划作业服务
* @module @/api/sys/job
*/
import config from '@/config'
import http from '@/utils/request'
export default {
/**
* 批量删除计划作业
*/
bulkDelete: {
url: `${config.API_URL}/api/sys/job/bulk.delete`,
name: `批量删除计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 创建计划作业
*/
create: {
url: `${config.API_URL}/api/sys/job/create`,
name: `创建计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 删除计划作业
*/
delete: {
url: `${config.API_URL}/api/sys/job/delete`,
name: `删除计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 计划作业是否存在
*/
exist: {
url: `${config.API_URL}/api/sys/job/exist`,
name: `计划作业是否存在`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 获取单个计划作业
*/
get: {
url: `${config.API_URL}/api/sys/job/get`,
name: `获取单个计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 分页查询计划作业
*/
pagedQuery: {
url: `${config.API_URL}/api/sys/job/paged.query`,
name: `分页查询计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 查询计划作业
*/
query: {
url: `${config.API_URL}/api/sys/job/query`,
name: `查询计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 启用/禁用作业
*/
setEnabled: {
url: `${config.API_URL}/api/sys/job/set.enabled`,
name: `启用/禁用作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
/**
* 更新计划作业
*/
update: {
url: `${config.API_URL}/api/sys/job/update`,
name: `更新计划作业`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
}

View File

@ -0,0 +1,10 @@
<template>
<svg t="1706693297583" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4279" width="128" height="128">
<path
d="M553 186.4v-84h204.8c22.5 0 41-18.4 41-41 0-22.5-18.4-41-41-41H266.2c-22.5 0-41 18.4-41 41 0 22.5 18.4 41 41 41H471v84C264 207 102.4 381.5 102.4 593.9c0 226.2 183.4 409.6 409.6 409.6s409.6-183.4 409.6-409.6c0-212.4-161.7-387-368.6-407.5z m-41 735.2c-180.7 0-327.7-147-327.7-327.7s147-327.7 327.7-327.7 327.7 147 327.7 327.7-147 327.7-327.7 327.7z"
p-id="4280"></path>
<path
d="M532.5 556.5V368.6c0-22.5-18.4-41-41-41s-41 18.4-41 41v204.8c0 0.3 0.1 0.5 0.1 0.7 0 2.4 0.3 4.9 0.7 7.3 0.2 0.9 0.5 1.7 0.8 2.6 0.5 1.7 0.9 3.5 1.6 5.2 0.2 0.5 0.5 1 0.8 1.5 2 4.2 4.5 8.2 8 11.6l173.8 173.8c15.9 15.9 42 15.9 57.9 0 15.9-15.9 15.9-42 0-57.9L532.5 556.5z"
p-id="4281"></path>
</svg>
</template>

View File

@ -53,3 +53,4 @@ export { default as ExLog } from './ExLog.vue'
export { default as Key } from './Key.vue'
export { default as OpenDoor } from './OpenDoor.vue'
export { default as Alert } from './Alert.vue'
export { default as ScheduledJob } from './ScheduledJob.vue'

File diff suppressed because one or more lines are too long

View File

@ -6,6 +6,7 @@ html {
height: 100%;
background-color: #f6f8f9;
font-size: 13px;
font-family: monospace;
}
a {

View File

@ -0,0 +1,229 @@
<template>
<el-container>
<el-header>
<div class="left-panel">
<na-search
:controls="[
{
type: 'input',
field: ['root', 'keywords'],
placeholder: $t('作业编号 / 作业名称'),
style: 'width:20rem',
},
{
type: 'select',
field: ['dy', 'status'],
options: Object.entries(this.$GLOBAL.enums.jobStatues).map((x) => {
return { value: x[0], label: x[1][1] }
}),
placeholder: $t('作业状态'),
style: 'width:15rem',
},
{
type: 'select',
field: ['dy', 'httpMethod'],
options: Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => {
return { value: x[0], label: x[1][1] }
}),
placeholder: $t('请求方式'),
style: 'width:15rem',
},
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '状态',
style: 'width:15rem',
},
]"
:vue="this"
@search="onSearch" />
</div>
<div class="right-panel">
<na-button-add :vue="this" />
<el-button :disabled="selection.length === 0" icon="el-icon-delete" plain type="danger" @click="batchDel"></el-button>
</div>
</el-header>
<el-main class="nopadding">
<sc-table
ref="table"
v-loading="loading"
:apiObj="$API.sys_job.pagedQuery"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:params="query"
remote-filter
remote-sort
stripe
@selection-change="
(items) => {
selection = items
}
">
<el-table-column type="selection"></el-table-column>
<el-table-column :label="$t('作业编号')" prop="id" width="150" />
<el-table-column :label="$t('作业名称')" prop="jobName" />
<na-col-indicator
:label="$t('作业状态')"
:options="[
{ text: '空闲', type: 'success', value: 'idle' },
{ text: '运行', type: 'warning', value: 'running' },
]"
prop="status"
width="100"></na-col-indicator>
<na-col-indicator
:label="$t('请求方式')"
:options="
Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => {
return { value: x[0], text: x[1][1] }
})
"
prop="httpMethod" />
<el-table-column :label="$t('上次执行时间')" prop="lastExecTime" sortable="custom" />
<el-table-column :label="$t('上次执行状态')" prop="lastStatusCode" sortable="custom" />
<el-table-column :label="$t('下次执行时间')" prop="nextExecTime" sortable="custom" />
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" />
<el-table-column :label="$t('启用')" prop="enabled">
<template #default="scope">
<el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
</template>
</el-table-column>
<na-col-operation
:buttons="
naColOperation.buttons.concat({
icon: 'el-icon-delete',
confirm: true,
title: $t('删除作业'),
click: rowDel,
})
"
:vue="this" />
</sc-table>
</el-main>
</el-container>
<save-dialog
v-if="dialog.save"
ref="saveDialog"
@closed="dialog.save = false"
@success="(data, mode) => table.handleUpdate($refs.table, data, mode)"></save-dialog>
</template>
<script>
import saveDialog from './save'
import table from '@/config/table'
import naColOperation from '@/config/naColOperation'
export default {
components: {
saveDialog,
},
data() {
return {
loading: false,
query: {
dynamicFilter: {
filters: [],
},
filter: {},
},
dialog: {
save: false,
},
selection: [],
}
},
watch: {},
computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
mounted() {},
created() {},
methods: {
//
async changeSwitch(event, row) {
try {
await this.$API.sys_job.setEnabled.post(row)
this.$message.success(`操作成功`)
} catch {
//
}
this.$refs.table.refresh()
},
//
async rowDel(row) {
try {
const res = await this.$API.sys_job.delete.post({ id: row.id })
this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
},
//
async batchDel() {
let loading
try {
await this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning',
})
loading = this.$loading()
const res = await this.$API.sys_job.bulkDelete.post({
items: this.selection,
})
this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
loading?.close()
},
//
onSearch(form) {
if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({
field: 'createdTime',
operator: 'dateRange',
value: form.dy.createdTime,
})
}
if (typeof form.dy.status === 'string' && form.dy.status.trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'status',
operator: 'eq',
value: form.dy.status,
})
}
if (typeof form.dy.enabled === 'boolean') {
this.query.dynamicFilter.filters.push({
field: 'enabled',
operator: 'eq',
value: form.dy.enabled,
})
}
if (typeof form.dy.httpMethod === 'string' && form.dy.httpMethod.trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'httpMethod',
operator: 'eq',
value: form.dy.httpMethod,
})
}
this.$refs.table.upData()
},
},
}
</script>
<style scoped></style>

View File

@ -0,0 +1,193 @@
<template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" destroy-on-close fullscreen @closed="$emit('closed')">
<el-form
ref="dialogForm"
v-loading="loading"
:disabled="mode === 'view'"
:model="form"
:rules="rules"
label-position="right"
label-width="150px">
<el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')">
<el-form-item v-if="mode === 'view'" :label="$t('作业编号')" prop="id">
<el-input v-model="form.id" clearable />
</el-form-item>
<el-form-item :label="$t('执行计划')" prop="executionCron">
<el-input v-model="form.executionCron" clearable />
</el-form-item>
<el-form-item :label="$t('请求方法')" prop="httpMethod">
<el-select v-model="form.httpMethod" clearable filterable>
<el-option v-for="(item, i) in $GLOBAL.enums.httpMethods" :key="i" :label="item[1]" :value="i" />
</el-select>
</el-form-item>
<el-form-item :label="$t('作业名称')" prop="jobName">
<el-input v-model="form.jobName" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('最近一次执行时间')" prop="lastExecTime">
<el-input v-model="form.lastExecTime" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('下一次执行时间')" prop="nextExecTime">
<el-input v-model="form.nextExecTime" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('下一次执行时间编号')" prop="nextTimeId">
<el-input v-model="form.nextTimeId" clearable />
</el-form-item>
<el-form-item :label="$t('请求头')" prop="requestHeader">
<el-input v-model="form.requestHeader" clearable rows="5" type="textarea" />
</el-form-item>
<el-form-item :label="$t('请求体')" prop="requestBody">
<el-input v-model="form.requestBody" clearable rows="5" type="textarea" />
</el-form-item>
<el-form-item :label="$t('请求的网络地址')" prop="requestUrl">
<el-input v-model="form.requestUrl" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('作业状态')" prop="status">
<el-input v-model="form.status" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('执行用户编号')" prop="userId">
<el-input v-model="form.userId" clearable />
</el-form-item>
<el-form-item v-else :label="$t('执行用户')" prop="userId">
<na-user v-model="form.userId"></na-user>
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('创建时间')" prop="createdTime">
<el-input v-model="form.createdTime" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('修改时间')" prop="modifiedTime">
<el-input v-model="form.modifiedTime" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('数据版本')" prop="version">
<el-input v-model="form.version" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('创建者编号')" prop="createdUserId">
<el-input v-model="form.createdUserId" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('创建者用户名')" prop="createdUserName">
<el-input v-model="form.createdUserName" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('修改者编号')" prop="modifiedUserId">
<el-input v-model="form.modifiedUserId" clearable />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('修改者用户名')" prop="modifiedUserName">
<el-input v-model="form.modifiedUserName" clearable />
</el-form-item>
</el-tab-pane>
<el-tab-pane v-if="mode === 'view'" :label="$t('原始数据')">
<json-viewer
:expand-depth="5"
:expanded="true"
:theme="this.$TOOL.data.get('APP_DARK') ? 'dark' : 'light'"
:value="form"
copyable
sort></json-viewer>
</el-tab-pane>
</el-tabs>
</el-form>
<template #footer>
<el-button @click="visible = false"> </el-button>
<el-button v-if="mode !== 'view'" :loading="loading" type="primary" @click="submit"> </el-button>
</template>
</sc-dialog>
</template>
<script>
import scEditor from '@/components/scEditor/index.vue'
export default {
components: {
scEditor,
},
emits: ['success', 'closed'],
data() {
return {
mode: 'add',
titleMap: {
view: this.$t('查看作业'),
add: this.$t('新增作业'),
edit: this.$t('编辑作业'),
},
visible: false,
loading: false,
//
form: {},
//
rules: {
executionCron: [
{
required: true,
pattern: this.$GLOBAL.chars.RGX_CRON,
message: this.$t('执行计划不正确'),
},
],
httpMethod: [
{
required: true,
message: this.$t('请求方不能为空'),
},
],
jobName: [
{
required: true,
message: this.$t('作业名称不能为空'),
},
],
requestUrl: [
{
required: true,
pattern: this.$GLOBAL.chars.RGX_URL,
message: this.$t('请求的网络地址不正确'),
},
],
userId: [
{
required: true,
trigger: 'blur',
message: this.$t('执行用户不能为空'),
validator: (rule, value, callback) => {
if (value.id) callback()
else callback(new Error())
},
},
],
},
}
},
mounted() {},
methods: {
//
async open(mode = 'add', data) {
this.visible = true
this.loading = true
this.mode = mode
if (data) {
const res = await this.$API.sys_job.get.post({ id: data.id })
Object.assign(this.form, res.data)
}
this.loading = false
return this
},
//
async submit() {
const valid = await this.$refs.dialogForm.validate().catch(() => {})
if (!valid) {
return false
}
try {
const method = this.mode === 'add' ? this.$API.sys_job.create : this.$API.sys_job.update
this.loading = true
const res = await method.post(Object.assign({}, this.form, { userId: this.form.userId.id }))
this.loading = false
this.$emit('success', res.data, this.mode)
this.visible = false
this.$message.success('操作成功')
} catch {
//
this.loading = false
}
},
},
}
</script>