diff --git a/Src/Dlhis.Basic/Dlhis.Basic.csproj b/Src/Dlhis.Basic/Dlhis.Basic.csproj
index 2dac3683..2d3bb2ea 100644
--- a/Src/Dlhis.Basic/Dlhis.Basic.csproj
+++ b/Src/Dlhis.Basic/Dlhis.Basic.csproj
@@ -18,7 +18,7 @@
-
+
diff --git a/Src/Dlhis.Basic/MealUser/Services/Implements/MealUserService.cs b/Src/Dlhis.Basic/MealUser/Services/Implements/MealUserService.cs
index 7c6d1b27..abb698f6 100644
--- a/Src/Dlhis.Basic/MealUser/Services/Implements/MealUserService.cs
+++ b/Src/Dlhis.Basic/MealUser/Services/Implements/MealUserService.cs
@@ -45,7 +45,7 @@ public class MealUserService : IMealUserService
UserNo = request.UserNo,
UserType = request.UserType,
UserCode = gainfoEntity.Code,
- CarNo = gainfoEntity.CarNo,
+ CarNo = gainfoEntity.CardNo,
Balance = 0,
BalanceOfRecharge = 0,
BalanceOfSubsidies = 0,
diff --git a/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs b/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs
index 825e05ce..c18beb24 100644
--- a/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs
+++ b/Src/Dlhis.Basic/Organization/Controllers/GainfosController.cs
@@ -1,4 +1,6 @@
-using Dlhis.Basic.Organization.Request;
+using System.ComponentModel.DataAnnotations;
+using Dlhis.Basic.Organization.Filters;
+using Dlhis.Basic.Organization.Request;
using Dlhis.Basic.Organization.Response;
using Dlhis.Basic.Organization.Services;
using Kurisu.AspNetCore.MVC;
@@ -96,7 +98,20 @@ public class GainfosController : ControllerBase
[HttpPost("face")]
public async Task UploadFaceAsync(GainfoUploadFaceRequest request)
{
- await _gainfoService.UploadFaceAsync(request);
+ await _gainfoService.UploadFaceAsync(request);
+ }
+
+ ///
+ /// 获取人脸照片
+ ///
+ ///
+ ///
+ [NotFound]
+ [AllowAnonymous]
+ [HttpGet("face")]
+ public async Task GetFaceAsync(string token)
+ {
+ return await _gainfoService.GetFaceAsync(token);
}
}
diff --git a/Src/Dlhis.Basic/Organization/Filters/NotFoundAttribute.cs b/Src/Dlhis.Basic/Organization/Filters/NotFoundAttribute.cs
new file mode 100644
index 00000000..b622d7cb
--- /dev/null
+++ b/Src/Dlhis.Basic/Organization/Filters/NotFoundAttribute.cs
@@ -0,0 +1,30 @@
+using Kurisu.Core.Result;
+using Kurisu.Core.Result.Abstractions;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+
+namespace Dlhis.Basic.Organization.Filters;
+
+[AttributeUsage(AttributeTargets.Method)]
+public class NotFoundAttribute : Attribute, IAsyncResultFilter
+{
+ public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
+ {
+ if (context.Result is ObjectResult result)
+ {
+ var type = result.Value?.GetType();
+ if (type.IsGenericType && type.IsAssignableTo(typeof(IApiResult)))
+ {
+ var apiResult = result.Value as DefaultApiResult
diff --git a/Src/Dlhis.ScheduledTask/Program.cs b/Src/Dlhis.ScheduledTask/Program.cs
index 281db99e..a4040c1c 100644
--- a/Src/Dlhis.ScheduledTask/Program.cs
+++ b/Src/Dlhis.ScheduledTask/Program.cs
@@ -1,6 +1,7 @@
using Kurisu.AspNetCore.Startup;
using Kurisu.Startup;
-
+using Microsoft.AspNetCore.DataProtection;
+using StackExchange.Redis;
class Program
{
@@ -15,4 +16,15 @@ public class Startup : DefaultStartup
public Startup(IConfiguration configuration) : base(configuration)
{
}
+
+ public override void ConfigureServices(IServiceCollection services)
+ {
+ base.ConfigureServices(services);
+
+ //todo
+ var redis = ConnectionMultiplexer.Connect($"192.168.199.30:30115,defaultDatabase:1,password=zxc111");
+ services.AddDataProtection()
+ .SetApplicationName("basic")
+ .PersistKeysToStackExchangeRedis(redis, "DataProtection-Keys");
+ }
}
\ No newline at end of file
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs
index 6b72836c..1a8ba8ec 100644
--- a/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Controllers/DeviceTaskAssignmentsController.cs
@@ -32,4 +32,14 @@ public class DeviceTaskAssignmentsController : ControllerBase
{
return await _deviceTaskAssignmentService.GetPageAsync(request);
}
+
+ ///
+ /// 获取设备任务(按设备)
+ ///
+ ///
+ [HttpGet("device-tasks")]
+ public async Task> DeviceGetTasksAsync([FromQuery] DeviceTaskGetRequest request)
+ {
+ return await _deviceTaskAssignmentService.DeviceGetTasksAsync(request);
+ }
}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/FaceUrlOptions.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/FaceUrlOptions.cs
new file mode 100644
index 00000000..89d05a98
--- /dev/null
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/FaceUrlOptions.cs
@@ -0,0 +1,14 @@
+using Kurisu.Core.ConfigurableOptions.Attributes;
+
+namespace Dlhis.ScheduledTask.TaskAssignment;
+
+[Configuration]
+public class FaceUrlOptions
+{
+ public string Url { get; set; }
+
+ ///
+ /// 过期时间:秒
+ ///
+ public int Limit { get; set; }
+}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs
new file mode 100644
index 00000000..612c6eec
--- /dev/null
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Request/DeviceTaskGetRequest.cs
@@ -0,0 +1,26 @@
+using System.ComponentModel.DataAnnotations;
+using Kurisu.AspNetCore.Utils.Extensions;
+using Kurisu.Core.DataAccess.Dto;
+
+namespace Dlhis.ScheduledTask.TaskAssignment.Request;
+
+public class DeviceTaskGetRequest : PageDto, IValidatableObject
+{
+ ///
+ /// 品牌
+ ///
+ public string Brand { get; set; }
+
+ ///
+ /// 设备号
+ ///
+ public string DeviceUniqueNo { get; set; }
+
+ public IEnumerable Validate(ValidationContext validationContext)
+ {
+ if (Brand.IsEmpty() && DeviceUniqueNo.IsEmpty())
+ {
+ yield return new ValidationResult("设备品牌和设备唯一号不能都为空", new[] { nameof(Brand), nameof(DeviceUniqueNo) });
+ }
+ }
+}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceConnectionResponse.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceConnectionResponse.cs
new file mode 100644
index 00000000..492756a5
--- /dev/null
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceConnectionResponse.cs
@@ -0,0 +1,19 @@
+namespace Dlhis.ScheduledTask.TaskAssignment.Response;
+
+public class DeviceConnectionResponse
+{
+ ///
+ /// 品牌
+ ///
+ public string Brand { get; set; }
+
+ ///
+ /// 设备唯一号
+ ///
+ public string DeviceUniqueNo { get; set; }
+
+ ///
+ /// 连接信息
+ ///
+ public string ConnectionInfo { get; set; }
+}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskToDeviceResponse.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskToDeviceResponse.cs
new file mode 100644
index 00000000..ab6bd4ce
--- /dev/null
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/DeviceTaskToDeviceResponse.cs
@@ -0,0 +1,27 @@
+namespace Dlhis.ScheduledTask.TaskAssignment.Response;
+
+///
+/// 设备维度任务
+///
+public class DeviceTaskToDeviceByDeviceResponse
+{
+ ///
+ /// 品牌
+ ///
+ public string Brand { get; set; }
+
+ ///
+ /// 设备唯一号
+ ///
+ public string DeviceUniqueNo { get; set; }
+
+ ///
+ /// 连接信息
+ ///
+ public string DeviceConnectionInfo { get; set; }
+
+ ///
+ /// 任务
+ ///
+ public List Tasks { get; set; }
+}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Response/TaskCombineUserDimensionDto.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/TaskCombineUserDimensionDto.cs
new file mode 100644
index 00000000..a9725d53
--- /dev/null
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/TaskCombineUserDimensionDto.cs
@@ -0,0 +1,27 @@
+using Dlhis.Entity.Device.Enums;
+using Kurisu.AspNetCore.Utils.Extensions;
+
+namespace Dlhis.ScheduledTask.TaskAssignment.Response;
+
+public class TaskCombineUserDimensionDto
+{
+ ///
+ /// 任务id
+ ///
+ public long TaskId { get; set; }
+
+ ///
+ /// 任务类型
+ ///
+ public DeviceTaskEnumType Type { get; set; }
+
+ ///
+ /// 任务类型描述
+ ///
+ public string TypeDesc => Type.GetDescription();
+
+ ///
+ /// 用户下发信息
+ ///
+ public UserToDeviceResponse UserInfo { get; set; }
+}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Response/UserToDeviceResponse.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/UserToDeviceResponse.cs
new file mode 100644
index 00000000..4d8e3b79
--- /dev/null
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Response/UserToDeviceResponse.cs
@@ -0,0 +1,42 @@
+using Newtonsoft.Json;
+
+namespace Dlhis.ScheduledTask.TaskAssignment.Response;
+
+public class UserToDeviceResponse
+{
+ ///
+ /// 人员档案号
+ ///
+ public string UserNo { get; set; }
+
+ ///
+ /// 人员学工号
+ ///
+ public string UserCode { get; set; }
+
+ ///
+ /// 人员名称
+ ///
+ public string UserName { get; set; }
+
+ ///
+ /// 部门号
+ ///
+ public string DepartmentNo { get; set; }
+
+ ///
+ /// 部门名称
+ ///
+ public string DepartmentName { get; set; }
+
+
+ ///
+ /// 卡号
+ ///
+ public string CardNo { get; set; }
+
+ ///
+ /// 人脸
+ ///
+ public string Face { get; set; }
+}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs
index 578d3570..c38a81a4 100644
--- a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/IDeviceTaskAssignmentService.cs
@@ -7,4 +7,11 @@ namespace Dlhis.ScheduledTask.TaskAssignment.Services;
public interface IDeviceTaskAssignmentService : IScopeDependency
{
Task> GetPageAsync(DeviceTaskAssignmentRequest request);
+
+ ///
+ /// 设备获取任务
+ ///
+ ///
+ ///
+ Task> DeviceGetTasksAsync(DeviceTaskGetRequest request);
}
diff --git a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs
index 7aed6893..0952e3c8 100644
--- a/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs
+++ b/Src/Dlhis.ScheduledTask/TaskAssignment/Services/Implements/DeviceTaskAssignmentService.cs
@@ -1,32 +1,44 @@
-using Dlhis.Entity.Device.Entities;
+using System.Linq.Expressions;
+using Dlhis.Entity.Basic.Entity;
+using Dlhis.Entity.Device.Entities;
+using Dlhis.Entity.Device.Enums;
using Dlhis.ScheduledTask.TaskAssignment.Request;
using Dlhis.ScheduledTask.TaskAssignment.Response;
+using Kurisu.AspNetCore.Scope;
+using Kurisu.AspNetCore.UnifyResultAndValidation;
+using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Dto;
using Kurisu.SqlSugar.Extensions;
using Kurisu.SqlSugar.Services;
+using Microsoft.AspNetCore.DataProtection;
+using Microsoft.Extensions.Options;
+using SqlSugar;
namespace Dlhis.ScheduledTask.TaskAssignment.Services.Implements;
public class DeviceTaskAssignmentService : IDeviceTaskAssignmentService
{
private readonly IDbContext _dbContext;
+ private readonly ILogger _logger;
- public DeviceTaskAssignmentService(IDbContext dbContext)
+ public DeviceTaskAssignmentService(IDbContext dbContext, ILogger logger)
{
_dbContext = dbContext;
+ _logger = logger;
}
public async Task> GetPageAsync(DeviceTaskAssignmentRequest request)
{
var page = await _dbContext.Queryable()
- .Select((deviceTask) => new DeviceTaskAssignmentResponse
+ .LeftJoin((deviceTask, ga) => deviceTask.UserNo == ga.No)
+ .Select((deviceTask, ga) => new DeviceTaskAssignmentResponse
{
DeviceUniqueNo = deviceTask.DeviceUniqueNo,
ResultMessage = deviceTask.ResultMessage,
RetryCount = deviceTask.RetryCount,
Status = deviceTask.Status,
UserNo = deviceTask.UserNo,
- UserName = deviceTask.UserName,
+ UserName = ga.Name,
UserCode = deviceTask.UserCode,
Type = deviceTask.Type,
LastUpdateTime = deviceTask.ModifiedTime
@@ -35,4 +47,137 @@ public class DeviceTaskAssignmentService : IDeviceTaskAssignmentService
return page;
}
+
+ public async Task> DeviceGetTasksAsync(DeviceTaskGetRequest request)
+ {
+ var data = await ContinueUpdateAsync(async () =>
+ {
+ var page = await _dbContext.Queryable()
+ .LeftJoin((deviceTask, deivce) => deviceTask.DeviceUniqueNo == deivce.UniqueNo)
+ .LeftJoin((deviceTask, deivce, deivceTamplate) => deivceTamplate.Code == deivce.TemplateCode)
+ .LeftJoin((deviceTask, deivce, deivceTamplate, ga) => deviceTask.UserNo == ga.No)
+ .LeftJoin((deviceTask, deivce, deivceTamplate, ga, department) => department.No == ga.DepartmentNo)
+ .WhereIF(request.Brand.IsPresent(), (deviceTask, deivce, deivceTamplate) => deivceTamplate.Brand == request.Brand)
+ .WhereIF(request.DeviceUniqueNo.IsPresent(), (deviceTask, deivce) => deviceTask.DeviceUniqueNo == request.DeviceUniqueNo)
+ .Where(deviceTask => deviceTask.Status != DeviceTaskStatusEnumType.Success)
+ .OrderBy(deviceTask => deviceTask.CreateTime)
+ .Select((deviceTask, deivce, deivceTamplate, ga, department) => new
+ {
+ deviceTask.Id,
+ deviceTask.Type,
+ deviceTask.DeviceUniqueNo,
+ DeviceConnectionInfo = deivce.ConnectionInfo,
+ deivce.Brand,
+ deviceTask.UserNo,
+ deviceTask.UserCode,
+ UserName = ga.Name,
+ ga.DepartmentNo,
+ DepartmentName = department.Name,
+ ga.CardNo,
+ deviceTask.Version
+ })
+ .ToPageListAsync(request);
+
+ var taskIdVs = page.Data.Select(x => new { x.Id, x.Version }).ToList();
+ Expression> where = LinqExpressionExtension.False();
+ taskIdVs.ForEach(p =>
+ {
+ where = where.Or(x => x.Id == p.Id && x.Version == p.Version && x.Status != DeviceTaskStatusEnumType.Success);
+ });
+
+ await _dbContext.Client.Ado.BeginTranAsync();
+ try
+ {
+ var effecet = await _dbContext.Updateable()
+ .SetColumns(x => x.Version == x.Version + 1)
+ .SetColumns(x => x.Status == DeviceTaskStatusEnumType.Process)
+ .SetColumns(x => x.ModifiedTime == DateTime.Now)
+ .Where(where)
+ .ExecuteCommandAsync();
+
+ if (taskIdVs.Count != effecet)
+ {
+ throw new VersionExceptions("更新错误");
+ }
+
+ await _dbContext.Client.Ado.CommitTranAsync();
+ }
+ catch
+ {
+ await _dbContext.Client.Ado.RollbackTranAsync();
+ throw;
+ }
+
+ return page.Data;
+ }, 5);
+
+ return Scoped.Request.Value.Create((sp) =>
+ {
+ var faceUrlOptions = sp.GetService>().Value;
+ var dataProtectionProvider = sp.GetService();
+
+ var timeLimitedDataProtector = dataProtectionProvider
+ .CreateProtector("down:face")
+ .ToTimeLimitedDataProtector();
+
+ return data.GroupBy(x => new { x.Brand, x.DeviceUniqueNo, x.DeviceConnectionInfo })
+ .Select(x =>
+ {
+ var b = x.Key.Brand;
+ var d = x.Key.DeviceUniqueNo;
+ var dc = x.Key.DeviceConnectionInfo;
+
+ return new DeviceTaskToDeviceByDeviceResponse
+ {
+ Brand = b,
+ DeviceUniqueNo = d,
+ DeviceConnectionInfo = dc,
+ Tasks = x.AsEnumerable().Select(item =>
+ {
+ var key = $"face:{item.UserCode}";
+ var token = timeLimitedDataProtector.Protect(key, TimeSpan.FromSeconds(faceUrlOptions.Limit));
+
+ return new TaskCombineUserDimensionDto
+ {
+ TaskId = item.Id,
+ Type = item.Type,
+ UserInfo = new UserToDeviceResponse
+ {
+ UserNo = item.UserNo,
+ UserCode = item.UserCode,
+ UserName = item.UserName,
+ DepartmentNo = item.DepartmentNo,
+ DepartmentName = item.DepartmentName,
+ CardNo = item.CardNo,
+ Face = faceUrlOptions.Url + token
+ }
+ };
+ }).ToList()
+ };
+
+ }).ToList();
+ });
+ }
+
+
+ private async Task ContinueUpdateAsync(Func> func, int retry)
+ {
+ for (int i = 0; i < retry; i++)
+ {
+ try
+ {
+ return await func();
+ }
+ catch (Exception ex) when (ex is VersionExceptions)
+ {
+ _logger.LogWarning(ex, ex.Message);
+ }
+ catch (Exception)
+ {
+ throw;
+ }
+ }
+
+ throw new UserFriendlyException("操作失败,请重试");
+ }
}
diff --git a/Src/Dlhis.ScheduledTask/appsettings.Development.json b/Src/Dlhis.ScheduledTask/appsettings.Development.json
index 17b935d5..fbfb5dd0 100644
--- a/Src/Dlhis.ScheduledTask/appsettings.Development.json
+++ b/Src/Dlhis.ScheduledTask/appsettings.Development.json
@@ -5,6 +5,10 @@
"Microsoft.AspNetCore": "Warning"
}
},
+ "FaceUrlOptions": {
+ "Url": "http://localhost:5002/api/gainfos/face?token=",
+ "Limit": 7200
+ },
"SwaggerOptions": {
"Enable": true,
"Authority": "https://yikatong.isawesome.cn/oauth2",
diff --git a/Src/Dlhis.ScheduledTask/appsettings.json b/Src/Dlhis.ScheduledTask/appsettings.json
index c6eceea5..ae228d61 100644
--- a/Src/Dlhis.ScheduledTask/appsettings.json
+++ b/Src/Dlhis.ScheduledTask/appsettings.json
@@ -8,6 +8,7 @@
"SqlSugarOptions": {
"DefaultConnectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=yikatong;charset=utf8mb4;",
"SlaveConnectionString": [],
+ "FaceConnectionString": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=face;charset=utf8mb4;",
"Timeout": 30,
"SlowSqlTime": 1,
"EnableSqlLog": true
@@ -15,6 +16,10 @@
"ConnectionStrings": {
"HangfireConnection": "server=192.168.199.124;port=3306;userid=root;password=dlhis123;database=HangFire;charset=utf8mb4;Allow User Variables=true;"
},
+ "FaceUrlOptions": {
+ "Url": "https://yikatong.isawesome.cn/api/basic/gainfos/face?token=",
+ "Limit": 7200
+ },
"SwaggerOptions": {
"Enable": true,
"Authority": "http://identity.oauth.svc.cluster.local",