refactor 封装任务,调整人员获取

This commit is contained in:
2024-03-29 10:43:13 +08:00
parent 1d2572e617
commit e2c8dcf8d5
30 changed files with 976 additions and 1003 deletions

View File

@@ -0,0 +1,6 @@
namespace Kurisu.Scheduled.Abstractions;
public interface IJob
{
Task TodoAsync();
}

View File

@@ -0,0 +1,18 @@
namespace Kurisu.Scheduled.Attributes;
[AttributeUsage(AttributeTargets.Class)]
public class RecurringJobAttribute : Attribute
{
public RecurringJobAttribute(string uniqueJobKey, string cron)
{
UniqueJobKey = uniqueJobKey;
Cron = cron;
}
public string Name { get; set; }
public string UniqueJobKey { get; }
public string Cron { get; }
public string Queue { get; set; } = "default";
}

View File

@@ -0,0 +1,53 @@
using System.Reflection;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.Scheduled.Abstractions;
using Kurisu.Scheduled.Attributes;
using Kurisu.SqlSugar.Services;
namespace Kurisu.Scheduled;
public abstract class BaseJob : IJob
{
private readonly IServiceProvider _serviceProvider;
public BaseJob(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
protected ILogger Logger { get; set; }
protected IDbContext DbContext { get; set; }
public async Task TodoAsync()
{
using (var scope = _serviceProvider.CreateScope())
{
Logger = scope.ServiceProvider.GetService<ILogger<BaseJob>>();
DbContext = scope.ServiceProvider.GetService<IDbContext>();
var sqlSugarOptionsService = scope.ServiceProvider.GetService<ISqlSugarOptionsService>();
DbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
sqlSugarOptionsService.IgnoreTenant = true;
var name = this.GetType().GetCustomAttribute<RecurringJobAttribute>()!.Name;
Logger.LogDebug("[{name}]开始执行...", name);
try
{
await RunAsync(scope.ServiceProvider);
}
catch (Exception ex)
{
Logger.LogError(ex, "[{name}]执行失败:{message} {stacktrace}", name, ex.Message, ex.StackTrace);
}
finally
{
Logger.LogDebug("[{name}]执行结束...", name);
DbContext.Client.QueryFilter.Restore();
sqlSugarOptionsService.IgnoreTenant = false;
}
}
}
public abstract Task RunAsync(IServiceProvider serviceProvider);
}

View File

@@ -1,165 +0,0 @@
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
using SqlSugar;
using System.Linq.Expressions;
namespace Dlhis.ScheduledTask.CommonJobs;
public class CancelDuplicationTaskJob
{
private readonly IServiceProvider _serviceProvider;
private readonly ILogger<CancelDuplicationTaskJob> _logger;
public CancelDuplicationTaskJob(IServiceProvider serviceProvider, ILogger<CancelDuplicationTaskJob> logger)
{
_serviceProvider = serviceProvider;
_logger = logger;
}
public async Task RunAsync()
{
_logger.LogInformation("抵消任务开始处理");
using (var scope = _serviceProvider.CreateScope())
{
var optionService = scope.ServiceProvider.GetService<ISqlSugarOptionsService>();
var db = scope.ServiceProvider.GetService<IDbContext>();
db.Client.QueryFilter.ClearAndBackup<ITenantId>();
optionService.IgnoreTenant = true;
try
{
//人员下发任务相关
var taskTypes = new List<DeviceTaskEnumType>() { DeviceTaskEnumType.Add, DeviceTaskEnumType.Delete, DeviceTaskEnumType.Update };
var deviceTasks = await db.Queryable<DeviceTaskEntity>()
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)//没有被获取
.Where(x => taskTypes.Contains(x.Type))
.OrderBy(x => x.CreateTime, OrderByType.Desc)
.ToListAsync();
var groupData = deviceTasks.GroupBy(x => new { x.TenantId, x.UserNo, x.DeviceUniqueNo });
foreach (var deviceTaskPack in groupData)
{
var uniqueInfo = deviceTaskPack.Key;
var data = deviceTaskPack.ToList();
foreach (var item in data)
{
try
{
Expression<Func<DeviceTaskEntity, bool>> where = LinqExpressionExtension.False<DeviceTaskEntity>();
var ignoreData = new List<IdVersion>();
//最新一条更新人员信息
if (item.Type == DeviceTaskEnumType.Update && item.Status == DeviceTaskStatusEnumType.Wait)
{
//获取过往的更新人员任务
ignoreData = data.Where(x => x.Type == DeviceTaskEnumType.Update)
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => x.Id != item.Id)
.Where(x => item.CreateTime > x.CreateTime)
.Select(x => new IdVersion { Id = x.Id, Version = x.Version }).ToList();
}
//最新一条删除人员信息
else if (item.Type == DeviceTaskEnumType.Delete && item.Status == DeviceTaskStatusEnumType.Wait)
{
//存在成功下发的任务,此处可以直接把其他任务取消
if (await db.Queryable<DeviceTaskEntity>().Where(x => x.UserNo == item.UserNo)
.Where(x => x.DeviceUniqueNo == item.DeviceUniqueNo)
.Where(x => x.TenantId == item.TenantId).Where(x => x.Status == DeviceTaskStatusEnumType.Success).AnyAsync())
{
ignoreData.AddRange(data.Where(x => x.Type == DeviceTaskEnumType.Add
|| x.Type == DeviceTaskEnumType.Delete
|| x.Type == DeviceTaskEnumType.Update)
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => x.Id != item.Id)
.Where(x => item.CreateTime > x.CreateTime)
.Select(x => new IdVersion { Id = x.Id, Version = x.Version })
);
}
else
{
var currentIds = new List<long> { item.Id };
var beforeTask = data.Where(x => x.Type == DeviceTaskEnumType.Add || x.Type == DeviceTaskEnumType.Update)
.Where(x => !currentIds.Contains(x.Id))
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => item.CreateTime > x.CreateTime)
.FirstOrDefault();
//逻辑正确,最终beforeTask.type == DeviceTaskEnumType.Add
while (beforeTask != null && beforeTask.Type == DeviceTaskEnumType.Update)
{
ignoreData.Add(new IdVersion() { Id = beforeTask.Id, Version = beforeTask.Version });
currentIds.Add(beforeTask.Id);
beforeTask = data.Where(x => x.Type == DeviceTaskEnumType.Add || x.Type == DeviceTaskEnumType.Update)
.Where(x => !currentIds.Contains(x.Id))
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => item.CreateTime > x.CreateTime)
.FirstOrDefault();
}
if (beforeTask != null)
{
ignoreData.Add(new IdVersion() { Id = beforeTask.Id, Version = beforeTask.Version });
ignoreData.Add(new IdVersion() { Id = item.Id, Version = item.Version });
}
}
}
if (ignoreData.Any())
{
ignoreData.ForEach(p =>
{
where = where.Or(x => x.Id == p.Id && x.Version == p.Version && x.Status == DeviceTaskStatusEnumType.Wait);
});
await db.Updateable<DeviceTaskEntity>()
.SetColumns(x => x.Version == x.Version + 1)
.SetColumns(x => x.Status == DeviceTaskStatusEnumType.Cancel)
.SetColumns(x => x.CancellationId == item.Id)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(where)
.ExecuteCommandAsync();
data.ForEach(x =>
{
if (ignoreData.Any(p => p.Id == x.Id))
x.Status = DeviceTaskStatusEnumType.Cancel;
});
}
}
catch (Exception ex)
{
_logger.LogError(ex, "抵消处理异常:{message}", ex.Message);
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "抵消处理异常:{message}", ex.Message);
}
finally
{
db.Client.QueryFilter.Restore();
optionService.IgnoreTenant = false;
}
}
_logger.LogInformation("抵消任务处理完成");
}
class IdVersion
{
public long Id { get; set; }
public int Version { get; set; }
}
}

View File

@@ -1,31 +0,0 @@
using Dlhis.Entity.Common;
using Kurisu.SqlSugar.Services;
namespace Dlhis.ScheduledTask.CommonJobs;
public class DelApiLogTaskJob
{
private readonly IDbContext _dbContext;
private readonly ILogger<DelApiLogTaskJob> _logger;
public DelApiLogTaskJob(IDbContext dbContext, ILogger<DelApiLogTaskJob> logger)
{
_dbContext = dbContext;
_logger = logger;
}
public async Task RunAsync()
{
try
{
DateTime before = DateTime.Now.Date.AddDays(-7);
await _dbContext.ChangeDb("log").Client.Deleteable<ApiLogEntity>().Where(x => x._ts <= before).ExecuteCommandAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "删除日志异常:{message}", ex.Message);
}
}
}

View File

@@ -1,52 +0,0 @@
using System.Net;
using System.Net.Sockets;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
using Newtonsoft.Json;
namespace Dlhis.ScheduledTask.CommonJobs;
public class HeartbeatJob
{
private readonly IDbContext _dbContext;
public HeartbeatJob(IDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task RunAsync()
{
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
try
{
var devices = await _dbContext.Queryable<DeviceEntity>().Where(x => x.Enable).Where(x => x.HeartbeatInterval > 0).ToListAsync();
foreach (var device in devices)
{
try
{
var ipInfo = JsonConvert.DeserializeObject<IPPortDto>(device.ConnectionInfo);
//超时时间
var CancellationTokenSource = new CancellationTokenSource(300);
await new TcpClient().ConnectAsync(IPAddress.Parse(ipInfo.IP), ipInfo.Port, CancellationTokenSource.Token);
await _dbContext.Updateable<DeviceRegistryEntity>().SetColumns(x => x.LastHeartbeatTime == DateTime.Now)
.Where(x => x.DeviceUniqueNo == device.UniqueNo).ExecuteCommandAsync();
// _logger.LogInformation("心跳设备成功:{no}", device.UniqueNo);
}
catch (Exception)
{
//_logger.LogError(ex, "心跳设备失败:{no}. {message}", device.UniqueNo, ex.Message);
}
}
}
finally
{
_dbContext.Client.QueryFilter.Restore();
}
}
}

View File

@@ -1,162 +0,0 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Basic.Enums;
using Dlhis.Entity.Canteen.Entities;
using Dlhis.ScheduledTask.SyncSapInfo;
using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
using SqlSugar;
namespace Dlhis.ScheduledTask.CommonJobs;
public class MealSupplementHandlerJob
{
private readonly IDbContext _dbContext;
private readonly ILogger<MealSupplementHandlerJob> _logger;
private readonly IJinkoJIPService _jinkoJIPService;
public MealSupplementHandlerJob(IDbContext dbContext, ILogger<MealSupplementHandlerJob> logger, IJinkoJIPService jinkoJIPService)
{
_dbContext = dbContext;
_logger = logger;
_jinkoJIPService = jinkoJIPService;
}
public async Task RunAsync()
{
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
try
{
//查询待处理餐补
var mealSupplements = await _dbContext.Queryable<MealSupplementRecordEntity>()
.Where(x => (x.Status == MealSupplementStatusEnumtype.Wait || x.Status == MealSupplementStatusEnumtype.Error) && !x.Sync)
.ToListAsync();
foreach (var item in mealSupplements)
{
GainfoMealEntity mealUser = null;
var gainfo = await _dbContext.Queryable<GainfoEntity>().Where(x => x.Code == item.Code).SingleAsync();
//档案不存在
if (gainfo == null)
{
//todo
//_logger.LogWarning("档案code:{code}不存在.flowno:{flowno}", item.Code, item.UniqueId);
//continue;
gainfo = new GainfoEntity() { No = Guid.NewGuid().ToString() };
}
else
{
}
mealUser = await _dbContext.Queryable<GainfoMealEntity>().Where(x => x.UserNo == gainfo.No).SingleAsync();
if (mealUser == null)
{
mealUser = new GainfoMealEntity
{
Balance = 0,
BalanceOfRecharge = 0,
BalanceOfSubsidies = 0,
UserNo = gainfo.No,
UserCode = gainfo.Code,
Version = 0,
TenantId = gainfo.Factory,
};
await _dbContext.InsertAsync(mealUser);
}
await SetMealSupplementAsync(mealUser, item);
}
}
finally
{
_dbContext.Client.QueryFilter.Restore();
}
}
private async Task SetMealSupplementAsync(GainfoMealEntity mealUser, MealSupplementRecordEntity mealSupplement)
{
await _dbContext.Client.Ado.BeginTranAsync();
try
{
await ContinueUpdateAsync(async () =>
{
mealUser = await _dbContext.Queryable<GainfoMealEntity>().Where(x => x.UserCode == mealUser.UserCode).FirstAsync();
var b = mealUser.Balance += mealSupplement.Amount;
var effect = await _dbContext.Updateable<GainfoMealEntity>()
.SetColumns(x => x.Balance == b)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UserCode == mealUser.UserCode && x.Version == mealUser.Version)
.ExecuteCommandAsync();
if (effect <= 0)
{
throw new VersionExceptions("餐补写入错误");
}
}, 5);
await _dbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Sucess)
.SetColumns(x => x.Sync == true)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
var r = await _jinkoJIPService.SyncMealSupplementRecordsAsync(new List<SyncMealSupplementRecordRequest>
{
new()
{
UniqueId = mealSupplement.UniqueId,
Status = "SUCCESS",
Msg = "操作成功"
}
});
await _dbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Result == r)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
await _dbContext.Client.Ado.CommitTranAsync();
}
catch (Exception ex)
{
await _dbContext.Client.Ado.RollbackTranAsync();
await _dbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Error)
.SetColumns(x => x.Result == ex.Message)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
}
}
private async Task ContinueUpdateAsync(Func<Task> func, int retry, string errorMessage = null)
{
for (int i = 0; i < retry; i++)
{
try
{
await func();
return;
}
catch (Exception ex) when (ex is VersionExceptions)
{
_logger.LogWarning(ex, ex.Message);
}
catch (Exception)
{
throw;
}
}
throw new UserFriendlyException(errorMessage ?? "操作失败,请重试");
}
}

View File

@@ -1,125 +0,0 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Sys.Entities;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
using Microsoft.AspNetCore.Identity;
namespace Dlhis.ScheduledTask.CommonJobs;
public class UpdateUserPasswordJob
{
private readonly IDbContext _dbContext;
private readonly ILogger<UpdateUserPasswordJob> _logger;
public UpdateUserPasswordJob(
IDbContext dbContext,
ILogger<UpdateUserPasswordJob> logger)
{
_dbContext = dbContext;
_logger = logger;
}
public async Task RunAsync()
{
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
try
{
var identityDbContext = _dbContext.ChangeDb("identity");
var whitelist = new List<string>() { "admin", "WB-0741", "WB-1091", "0123456" };
var users = await identityDbContext.Queryable<UserEntity>()
.Where(x => !whitelist.Contains(x.UserName))
.ToListAsync();
var gainfos = await _dbContext.Queryable<GainfoEntity>().Where(x => !whitelist.Contains(x.Code)).ToListAsync();
var hasher = new PasswordHasher<UserEntity>();
foreach (var gainfo in gainfos)
{
if (gainfo.IdNo.IsPresent())
{
try
{
var password = gainfo.IdNo.TakeLast(6).Join("");
var user = users.FirstOrDefault(x => x.UserName == gainfo.Code);
if (user == null)
{
user = new UserEntity
{
UserName = gainfo.Code,
NormalizedUserName = gainfo.Code.ToUpper(),
ConcurrencyStamp = Guid.NewGuid().ToString(),
SecurityStamp = Guid.NewGuid().ToString(),
};
password = hasher.HashPassword(user, password);
user.PasswordHash = password;
var id = (int)await identityDbContext.InsertReturnIdentityAsync(user);
await AddUserClaim(identityDbContext, id, "jinko-code", gainfo.Code);
await AddUserClaim(identityDbContext, id, "name", gainfo.Name);
if (!string.IsNullOrEmpty(gainfo.Factory))
{
var userTenant = await identityDbContext.Queryable<UserTenantEntity>().Where(x => x.UserId == user.Id).FirstAsync();
if (userTenant == null)
{
userTenant = new UserTenantEntity
{
UserId = user.Id,
TenantCode = gainfo.Factory
};
await identityDbContext.InsertAsync(userTenant);
}
else
{
userTenant.TenantCode = gainfo.Factory;
await identityDbContext.UpdateAsync(userTenant);
}
}
}
else
{
password = hasher.HashPassword(user, password);
user.PasswordHash = password;
await identityDbContext.UpdateAsync(user);
}
}
catch (Exception)
{
}
}
}
}
catch (Exception ex)
{
_logger.LogError(ex, "密码修改失败:{message}", ex.Message);
}
finally
{
_dbContext.Client.QueryFilter.Restore();
}
}
private async Task AddUserClaim(IDbContext dbContext, int userId, string type, string value)
{
var claim = await dbContext.Queryable<UserClaimEntity>().Where(x => x.UserId == userId).FirstAsync(x => x.ClaimType == type);
if (claim == null)
{
claim = new UserClaimEntity()
{
UserId = userId,
ClaimType = type,
ClaimValue = value
};
await dbContext.InsertAsync(claim);
}
else
{
claim.ClaimValue = value;
await dbContext.UpdateAsync(claim);
}
}
}

View File

@@ -21,11 +21,11 @@
<PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" /> <PackageReference Include="Hangfire.Dashboard.BasicAuthorization" Version="1.0.2" />
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.9.3" /> <PackageReference Include="Hangfire.Redis.StackExchange" Version="1.9.3" />
<PackageReference Include="Kurisu.AspNetCore" Version="0.7.0" /> <PackageReference Include="Kurisu.AspNetCore" Version="0.7.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.4" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Dlhis.Basic.Api\Dlhis.Basic.Api.csproj" /> <ProjectReference Include="..\Dlhis.Basic.Api\Dlhis.Basic.Api.csproj" />
<ProjectReference Include="..\Dlhis.Canteen.Api\Dlhis.Canteen.Api.csproj" />
<ProjectReference Include="..\Dlhis.Entity\Dlhis.Entity.csproj" /> <ProjectReference Include="..\Dlhis.Entity\Dlhis.Entity.csproj" />
<ProjectReference Include="..\Dlhis.Sys.Api\Dlhis.Sys.Api.csproj" /> <ProjectReference Include="..\Dlhis.Sys.Api\Dlhis.Sys.Api.csproj" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,138 @@
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using SqlSugar;
using System.Linq.Expressions;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 处理重复下发任务
/// </summary>
[RecurringJob(nameof(CancelDuplicationTaskJob), "0/15 * * * * ?", Name = "处理重复下发任务")]
public class CancelDuplicationTaskJob : BaseJob
{
public CancelDuplicationTaskJob(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
//人员下发任务相关
var taskTypes = new List<DeviceTaskEnumType>() { DeviceTaskEnumType.Add, DeviceTaskEnumType.Delete, DeviceTaskEnumType.Update };
var deviceTasks = await DbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)//没有被获取
.Where(x => taskTypes.Contains(x.Type))
.OrderBy(x => x.CreateTime, OrderByType.Desc)
.ToListAsync();
var groupData = deviceTasks.GroupBy(x => new { x.TenantId, x.UserNo, x.DeviceUniqueNo });
foreach (var deviceTaskPack in groupData)
{
var uniqueInfo = deviceTaskPack.Key;
var data = deviceTaskPack.ToList();
foreach (var item in data)
{
try
{
Expression<Func<DeviceTaskEntity, bool>> where = LinqExpressionExtension.False<DeviceTaskEntity>();
var ignoreData = new List<IdVersion>();
//最新一条更新人员信息
if (item.Type == DeviceTaskEnumType.Update && item.Status == DeviceTaskStatusEnumType.Wait)
{
//获取过往的更新人员任务
ignoreData = data.Where(x => x.Type == DeviceTaskEnumType.Update)
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => x.Id != item.Id)
.Where(x => item.CreateTime > x.CreateTime)
.Select(x => new IdVersion { Id = x.Id, Version = x.Version }).ToList();
}
//最新一条删除人员信息
else if (item.Type == DeviceTaskEnumType.Delete && item.Status == DeviceTaskStatusEnumType.Wait)
{
//存在成功下发的任务,此处可以直接把其他任务取消
if (await DbContext.Queryable<DeviceTaskEntity>().Where(x => x.UserNo == item.UserNo)
.Where(x => x.DeviceUniqueNo == item.DeviceUniqueNo)
.Where(x => x.TenantId == item.TenantId).Where(x => x.Status == DeviceTaskStatusEnumType.Success).AnyAsync())
{
ignoreData.AddRange(data.Where(x => x.Type == DeviceTaskEnumType.Add
|| x.Type == DeviceTaskEnumType.Delete
|| x.Type == DeviceTaskEnumType.Update)
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => x.Id != item.Id)
.Where(x => item.CreateTime > x.CreateTime)
.Select(x => new IdVersion { Id = x.Id, Version = x.Version })
);
}
else
{
var currentIds = new List<long> { item.Id };
var beforeTask = data.Where(x => x.Type == DeviceTaskEnumType.Add || x.Type == DeviceTaskEnumType.Update)
.Where(x => !currentIds.Contains(x.Id))
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => item.CreateTime > x.CreateTime)
.FirstOrDefault();
//逻辑正确,最终beforeTask.type == DeviceTaskEnumType.Add
while (beforeTask != null && beforeTask.Type == DeviceTaskEnumType.Update)
{
ignoreData.Add(new IdVersion() { Id = beforeTask.Id, Version = beforeTask.Version });
currentIds.Add(beforeTask.Id);
beforeTask = data.Where(x => x.Type == DeviceTaskEnumType.Add || x.Type == DeviceTaskEnumType.Update)
.Where(x => !currentIds.Contains(x.Id))
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait)
.Where(x => item.CreateTime > x.CreateTime)
.FirstOrDefault();
}
if (beforeTask != null)
{
ignoreData.Add(new IdVersion() { Id = beforeTask.Id, Version = beforeTask.Version });
ignoreData.Add(new IdVersion() { Id = item.Id, Version = item.Version });
}
}
}
if (ignoreData.Any())
{
ignoreData.ForEach(p =>
{
where = where.Or(x => x.Id == p.Id && x.Version == p.Version && x.Status == DeviceTaskStatusEnumType.Wait);
});
await DbContext.Updateable<DeviceTaskEntity>()
.SetColumns(x => x.Version == x.Version + 1)
.SetColumns(x => x.Status == DeviceTaskStatusEnumType.Cancel)
.SetColumns(x => x.CancellationId == item.Id)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(where)
.ExecuteCommandAsync();
data.ForEach(x =>
{
if (ignoreData.Any(p => p.Id == x.Id))
x.Status = DeviceTaskStatusEnumType.Cancel;
});
}
}
catch (Exception ex)
{
Logger.LogError(ex, "抵消处理异常:{message}", ex.Message);
}
}
}
}
class IdVersion
{
public long Id { get; set; }
public int Version { get; set; }
}
}

View File

@@ -5,74 +5,62 @@ using Dlhis.Sys.Api;
using Dlhis.Sys.Api.Response; using Dlhis.Sys.Api.Response;
using Kurisu.AspNetCore.Utils.Extensions; using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Entity; using Kurisu.Core.DataAccess.Entity;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using Kurisu.SqlSugar.Services; using Kurisu.SqlSugar.Services;
using Newtonsoft.Json; using Newtonsoft.Json;
using SqlSugar; using SqlSugar;
namespace Dlhis.ScheduledTask.CommonJobs; namespace Dlhis.ScheduledTask.Jobs;
public class CreateDefinitelyGroupJob /// <summary>
/// 创建系统默认分组
/// </summary>
[RecurringJob(nameof(CreateDefinitelyGroupJob), "0 0 5 * * ?", Name = "创建系统默认分组")]
public class CreateDefinitelyGroupJob : BaseJob
{ {
private readonly ILogger<CreateDefinitelyGroupJob> _logger;
private readonly ITenantApi _tenantApi; private readonly ITenantApi _tenantApi;
private readonly IDbContext _dbContext;
public CreateDefinitelyGroupJob(ILogger<CreateDefinitelyGroupJob> logger, public CreateDefinitelyGroupJob(
ITenantApi tenantApi, ITenantApi tenantApi,
IDbContext dbContext, IServiceProvider serviceProvider) : base(serviceProvider)
ISqlSugarOptionsService sqlSugarOptionsService)
{ {
_logger = logger;
_tenantApi = tenantApi; _tenantApi = tenantApi;
_dbContext = dbContext;
sqlSugarOptionsService.IgnoreTenant = true;
} }
private static readonly string _remark = "系统自动生成"; private static readonly string _remark = "系统自动生成";
public async Task RunAsync() public override async Task RunAsync(IServiceProvider serviceProvider)
{ {
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>(); //获取所有基地信息
try var tenantResult = await _tenantApi.GetTenantsInternalAsync();
{ var tenants = tenantResult.Data;
//获取所有基地信息
var tenantResult = await _tenantApi.GetTenantsInternalAsync();
var tenants = tenantResult.Data;
_logger.LogInformation("获取基地信息:{json}", JsonConvert.SerializeObject(tenants)); Logger.LogInformation("获取基地信息:{json}", JsonConvert.SerializeObject(tenants));
//****************************************************系统人员分组**********************************************************/ //****************************************************系统人员分组**********************************************************/
await CreateGainfoGroupsAsync(tenants); await CreateGainfoGroupsAsync(tenants);
//*****************************************************考勤设备组*********************************************************/ //*****************************************************考勤设备组*********************************************************/
await CreateDeviceGroupAsync(DeviceTypeEnumType.Attendance, new List<string> { "*通用考勤设备组", "*专用考勤设备组" }, tenants); await CreateDeviceGroupAsync(DeviceTypeEnumType.Attendance, new List<string> { "*通用考勤设备组", "*专用考勤设备组" }, tenants);
//*****************************************************门禁设备组********************************************************/ //*****************************************************门禁设备组********************************************************/
await CreateDeviceGroupAsync(DeviceTypeEnumType.Door, new List<string> { "*共用区域", "*非共用区域" }, tenants);
await CreateDeviceGroupAsync(DeviceTypeEnumType.Door, new List<string> { "*共用区域", "*非共用区域" }, tenants);
}
catch (Exception ex)
{
_logger.LogError(ex, "自动创建系统分组失败:{message}", ex.Message);
}
finally
{
_dbContext.Client.QueryFilter.Restore();
}
} }
private async Task CreateGainfoGroupsAsync(List<TenantResponse> tenants) private async Task CreateGainfoGroupsAsync(List<TenantResponse> tenants)
{ {
var grps = new List<string> { "*自定义人员分组", "*督查组", "*X3及以上领导组" }; var grps = new List<string> { "*自定义人员分组", "*督查组", "*X3及以上领导组" };
var searchGrps = await _dbContext.Queryable<GainfoGroupEntity>().Where(x => grps.Contains(x.Name)).ToListAsync(); var searchGrps = await DbContext.Queryable<GainfoGroupEntity>().Where(x => grps.Contains(x.Name)).ToListAsync();
foreach (var grp in grps) foreach (var grp in grps)
{ {
if (!searchGrps.Any(x => x.Name == grp)) if (!searchGrps.Any(x => x.Name == grp))
{ {
await _dbContext.InsertAsync(new GainfoGroupEntity await DbContext.InsertAsync(new GainfoGroupEntity
{ {
PCode = null, PCode = null,
Code = Guid.NewGuid(), Code = Guid.NewGuid(),
@@ -96,7 +84,7 @@ public class CreateDefinitelyGroupJob
{ {
foreach (var grp in topGroups) foreach (var grp in topGroups)
{ {
var topGrp = await _dbContext.Queryable<DeviceGroupEntity>().FirstAsync(x => x.Name == grp); var topGrp = await DbContext.Queryable<DeviceGroupEntity>().FirstAsync(x => x.Name == grp);
if (topGrp == null) if (topGrp == null)
{ {
topGrp = new DeviceGroupEntity topGrp = new DeviceGroupEntity
@@ -115,13 +103,13 @@ public class CreateDefinitelyGroupJob
ModifiedTime = DateTime.Now, ModifiedTime = DateTime.Now,
TenantId = null, TenantId = null,
}; };
await _dbContext.InsertAsync(topGrp); await DbContext.InsertAsync(topGrp);
} }
foreach (var workplace in tenants.Where(x => x.PCode.IsEmpty())) foreach (var workplace in tenants.Where(x => x.PCode.IsEmpty()))
{ {
var workplaceName = $"*{type.GetDescription()}工作地系统组({workplace.Name})"; var workplaceName = $"*{type.GetDescription()}工作地系统组({workplace.Name})";
var workplaceGrp = await _dbContext.Queryable<DeviceGroupEntity>().Where(x => x.PCode == topGrp.Code).FirstAsync(x => x.Name == workplaceName); var workplaceGrp = await DbContext.Queryable<DeviceGroupEntity>().Where(x => x.PCode == topGrp.Code).FirstAsync(x => x.Name == workplaceName);
if (workplaceGrp == null) if (workplaceGrp == null)
{ {
workplaceGrp = new DeviceGroupEntity workplaceGrp = new DeviceGroupEntity
@@ -140,15 +128,15 @@ public class CreateDefinitelyGroupJob
ModifiedTime = DateTime.Now, ModifiedTime = DateTime.Now,
TenantId = null, TenantId = null,
}; };
await _dbContext.InsertAsync(workplaceGrp); await DbContext.InsertAsync(workplaceGrp);
} }
foreach (var factory in tenants.Where(x => x.PCode == workplace.Code)) foreach (var factory in tenants.Where(x => x.PCode == workplace.Code))
{ {
var factoryName = $"*{type.GetDescription()}厂区系统组({factory.Name})"; var factoryName = $"*{type.GetDescription()}厂区系统组({factory.Name})";
if (!await _dbContext.Queryable<DeviceGroupEntity>().Where(x => x.PCode == workplaceGrp.Code).AnyAsync(x => x.Name == factoryName)) if (!await DbContext.Queryable<DeviceGroupEntity>().Where(x => x.PCode == workplaceGrp.Code).AnyAsync(x => x.Name == factoryName))
{ {
await _dbContext.InsertAsync(new DeviceGroupEntity await DbContext.InsertAsync(new DeviceGroupEntity
{ {
PCode = workplaceGrp.Code, PCode = workplaceGrp.Code,
Code = Guid.NewGuid(), Code = Guid.NewGuid(),

View File

@@ -0,0 +1,53 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 创建上传设备数据任务
/// </summary>
[RecurringJob(nameof(CreateGetUploadRecordJob), "0 0/1 * * * ?", Name = "创建上传设备数据任务")]
public class CreateGetUploadRecordJob : BaseJob
{
public CreateGetUploadRecordJob(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
var devices = await DbContext.Queryable<DeviceEntity>()
.Where(x => x.Enable)
.Where(x => x.Type == DeviceTypeEnumType.Attendance || x.Type == DeviceTypeEnumType.Door)
.ToListAsync();
//过滤处理中的任务
var currentUniqueNos = await DbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.Type == DeviceTaskEnumType.UploadRecord)
.Where(x => x.Status == DeviceTaskStatusEnumType.Process || x.Status == DeviceTaskStatusEnumType.Wait)
.Select(x => x.DeviceUniqueNo)
.ToListAsync();
var tasks = devices.Where(x => !currentUniqueNos.Contains(x.UniqueNo)).Select(x => new DeviceTaskEntity
{
DeviceUniqueNo = x.UniqueNo,
Status = DeviceTaskStatusEnumType.Wait,
TenantId = x.TenantId,
Version = 0,
Type = DeviceTaskEnumType.UploadRecord,
CreatedBy = 1,
CreateTime = DateTime.Now,
ModifiedBy = 1,
ModifiedTime = DateTime.Now,
})
.ToList();
await DbContext.InsertAsync(tasks);
}
}

View File

@@ -0,0 +1,54 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 设备时间同步
/// </summary>
[RecurringJob(nameof(CreateSyncServerDateTimeJob), "0 0 0/1 * * ?", Name = "设备时间同步")]
public class CreateSyncServerDateTimeJob : BaseJob
{
public CreateSyncServerDateTimeJob(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
var deviceUniqueNos = await DbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait || x.Status == DeviceTaskStatusEnumType.Process)
.Where(x => x.Type == DeviceTaskEnumType.SyncDateTime)
.Select(x => x.DeviceUniqueNo)
.Distinct()
.ToListAsync();
var devices = await DbContext.Queryable<DeviceEntity>()
.Where(x => !deviceUniqueNos.Contains(x.UniqueNo))
.Where(x => x.Enable)
.ToListAsync();
var tasks = devices.Select(x => new DeviceTaskEntity
{
DeviceUniqueNo = x.UniqueNo,
Status = DeviceTaskStatusEnumType.Wait,
TenantId = x.TenantId,
Version = 0,
UserNo = string.Empty,
Type = DeviceTaskEnumType.SyncDateTime,
CreatedBy = 1,
CreateTime = DateTime.Now,
ModifiedBy = 1,
ModifiedTime = DateTime.Now,
}).ToList();
await DbContext.InsertAsync(tasks);
}
}

View File

@@ -0,0 +1,25 @@
using Dlhis.Entity.Common;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 删除过期日志
/// </summary>
[RecurringJob(nameof(DeleteApiLogTaskJob), "0 0 0 */6 * ?", Name = "删除过期日志")]
public class DeleteApiLogTaskJob : BaseJob
{
public DeleteApiLogTaskJob(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
var before = DateTime.Now.Date.AddDays(-7);
await DbContext.ChangeDb("log").Client.Deleteable<ApiLogEntity>().Where(x => x._ts <= before).ExecuteCommandAsync();
}
}

View File

@@ -0,0 +1,42 @@
using System.Net;
using System.Net.Sockets;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using Newtonsoft.Json;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 设备心跳
/// </summary>
[RecurringJob(nameof(HeartbeatJob), "0/10 * * * * ?", Name = "设备心跳")]
public class HeartbeatJob : BaseJob
{
public HeartbeatJob(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
var devices = await DbContext.Queryable<DeviceEntity>().Where(x => x.Enable).Where(x => x.HeartbeatInterval > 0).ToListAsync();
foreach (var device in devices)
{
try
{
var ipInfo = JsonConvert.DeserializeObject<IPPortDto>(device.ConnectionInfo);
//超时时间
var CancellationTokenSource = new CancellationTokenSource(300);
await new TcpClient().ConnectAsync(IPAddress.Parse(ipInfo.IP), ipInfo.Port, CancellationTokenSource.Token);
await DbContext.Updateable<DeviceRegistryEntity>().SetColumns(x => x.LastHeartbeatTime == DateTime.Now)
.Where(x => x.DeviceUniqueNo == device.UniqueNo).ExecuteCommandAsync();
}
catch (Exception)
{
}
}
}
}

View File

@@ -0,0 +1,224 @@
using Dlhis.Canteen.Api;
using Dlhis.Canteen.Api.Request;
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Basic.Enums;
using Dlhis.Entity.Canteen.Entities;
using Dlhis.ScheduledTask.SyncSapInfo;
using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Kurisu.AspNetCore.UnifyResultAndValidation;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using SqlSugar;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 餐补核算
/// </summary>
[RecurringJob(nameof(MealSupplementHandlerJob), "0 0/30 * * * ?", Name = "餐补核算")]
public class MealSupplementHandlerJob : BaseJob
{
private readonly IHainingMealSupplementApi _hainingMealSupplementApi;
private readonly IJinkoJIPService _jinkoJIPService;
public MealSupplementHandlerJob(IServiceProvider serviceProvider, IHainingMealSupplementApi hainingMealSupplementApi, IJinkoJIPService jinkoJIPService) : base(serviceProvider)
{
_hainingMealSupplementApi = hainingMealSupplementApi;
_jinkoJIPService = jinkoJIPService;
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
//查询待处理餐补
var mealSupplements = await DbContext.Queryable<MealSupplementRecordEntity>()
.Where(x => (x.Status == MealSupplementStatusEnumtype.Wait || x.Status == MealSupplementStatusEnumtype.Error) && !x.Sync)
.ToListAsync();
foreach (var item in mealSupplements)
{
GainfoMealEntity mealUser = null;
var gainfo = await DbContext.Queryable<GainfoEntity>().Where(x => x.Code == item.Code).SingleAsync();
//档案不存在
if (gainfo == null)
{
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Error)
.SetColumns(x => x.Result == "人员档案不存在")
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == item.UniqueId)
.ExecuteCommandAsync();
continue;
}
else
{
//创建余额数据
mealUser = await DbContext.Queryable<GainfoMealEntity>().Where(x => x.UserCode == gainfo.Code).SingleAsync();
if (mealUser == null)
{
mealUser = new GainfoMealEntity
{
Balance = 0,
BalanceOfRecharge = 0,
BalanceOfSubsidies = 0,
UserNo = gainfo.No,
UserCode = gainfo.Code,
Version = 0,
TenantId = gainfo.Factory,
};
await DbContext.InsertAsync(mealUser);
}
}
if (item.Workplace == "5")
{
await SetHainingMealSupplementAsync(item);
}
else
{
await SetMealSupplementAsync(item);
}
}
}
private async Task SetMealSupplementAsync(MealSupplementRecordEntity mealSupplement)
{
await DbContext.Client.Ado.BeginTranAsync();
try
{
await ContinueUpdateAsync(async () =>
{
var mealUser = await DbContext.Queryable<GainfoMealEntity>().Where(x => x.UserCode == mealSupplement.Code).FirstAsync();
var b = mealUser.Balance += mealSupplement.Amount;
var effect = await DbContext.Updateable<GainfoMealEntity>()
.SetColumns(x => x.Balance == b)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UserCode == mealUser.UserCode && x.Version == mealUser.Version)
.ExecuteCommandAsync();
if (effect <= 0)
{
throw new VersionExceptions("餐补写入错误");
}
}, 5);
//更新餐补信息为成功(同步成功)
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Sucess)
.SetColumns(x => x.Sync == true)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
var r = await _jinkoJIPService.SyncMealSupplementRecordsAsync(new List<SyncMealSupplementRecordRequest>
{
new()
{
UniqueId = mealSupplement.UniqueId,
Status = "SUCCESS",
Msg = "操作成功"
}
});
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Result == r)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
await DbContext.Client.Ado.CommitTranAsync();
}
catch (Exception ex)
{
await DbContext.Client.Ado.RollbackTranAsync();
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Error)
.SetColumns(x => x.Result == ex.Message)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
}
}
private async Task SetHainingMealSupplementAsync(MealSupplementRecordEntity mealSupplement)
{
await DbContext.Client.Ado.BeginTranAsync();
try
{
var hnResponse = await _hainingMealSupplementApi.AddMealSupplementAsync(new AddHainingMealSupplementRequest
{
Code = mealSupplement.Code,
Amount = mealSupplement.Amount,
Date = mealSupplement.Date,
Remark = mealSupplement.Remark,
UniqueId = mealSupplement.UniqueId,
});
(hnResponse == null || hnResponse.Code != 200).ThrowIfTrue("海宁餐补写入失败:" + hnResponse?.Message);
//更新餐补信息为成功(同步成功)
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Sucess)
.SetColumns(x => x.Sync == true)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
var r = await _jinkoJIPService.SyncMealSupplementRecordsAsync(new List<SyncMealSupplementRecordRequest>
{
new()
{
UniqueId = mealSupplement.UniqueId,
Status = "SUCCESS",
Msg = "操作成功"
}
});
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Result == r)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
await DbContext.Client.Ado.CommitTranAsync();
}
catch (Exception ex)
{
await DbContext.Client.Ado.RollbackTranAsync();
await DbContext.Updateable<MealSupplementRecordEntity>()
.SetColumns(x => x.Status == MealSupplementStatusEnumtype.Error)
.SetColumns(x => x.Result == ex.Message)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => x.UniqueId == mealSupplement.UniqueId)
.ExecuteCommandAsync();
}
}
private async Task ContinueUpdateAsync(Func<Task> func, int retry, string errorMessage = null)
{
for (int i = 0; i < retry; i++)
{
try
{
await func();
return;
}
catch (Exception ex) when (ex is VersionExceptions)
{
Logger.LogWarning(ex, ex.Message);
}
catch (Exception)
{
throw;
}
}
throw new UserFriendlyException(errorMessage ?? "操作失败,请重试");
}
}

View File

@@ -2,25 +2,25 @@
using Dlhis.ScheduledTask.SyncSapInfo.Dto; using Dlhis.ScheduledTask.SyncSapInfo.Dto;
using Dlhis.ScheduledTask.SyncSapInfo.Services; using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Hangfire; using Hangfire;
using Kurisu.SqlSugar.Services; using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
namespace Dlhis.ScheduledTask.SyncSapInfo.Job; namespace Dlhis.ScheduledTask.Jobs;
/// <summary> /// <summary>
/// 部门同步定时任务 /// SAP组织架构数据同步
/// </summary> /// </summary>
public class SyncOrganizationJob [RecurringJob(nameof(SyncOrganizationJob), "0 0 0 * * ?", Name = "SAP组织架构数据同步", Queue = "SAP")]
public class SyncOrganizationJob : BaseJob
{ {
private readonly IDbContext _dbContext;
private readonly IJinkoSapService _jinkoSapService; private readonly IJinkoSapService _jinkoSapService;
public SyncOrganizationJob(IDbContext dbContext, IJinkoSapService jinkoSapService) public SyncOrganizationJob(IServiceProvider serviceProvider, IJinkoSapService jinkoSapService) : base(serviceProvider)
{ {
_dbContext = dbContext;
_jinkoSapService = jinkoSapService; _jinkoSapService = jinkoSapService;
} }
public async Task RunAsync() public override async Task RunAsync(IServiceProvider serviceProvider)
{ {
var orgResult = await _jinkoSapService.GetSapOrganizationsAsync(); var orgResult = await _jinkoSapService.GetSapOrganizationsAsync();
@@ -28,16 +28,13 @@ public class SyncOrganizationJob
{ {
await SaveOrg(orgResult.Org.Items); await SaveOrg(orgResult.Org.Items);
} }
var syncPositionJob = BackgroundJob.Enqueue<SyncPositionJob>(job => job.RunAsync());
BackgroundJob.ContinueJobWith<SyncPersonJob>(syncPositionJob, job => job.RunAsync(), JobContinuationOptions.OnlyOnSucceededState);
} }
private async Task SaveOrg(List<SapOrganizationItem> modifiedOrgList) private async Task SaveOrg(List<SapOrganizationItem> modifiedOrgList)
{ {
foreach (SapOrganizationItem item in modifiedOrgList) foreach (SapOrganizationItem item in modifiedOrgList)
{ {
var department = await _dbContext.Queryable<DepartmentEntity>().Where(x => x.No == item.OBJID).FirstAsync(); var department = await DbContext.Queryable<DepartmentEntity>().Where(x => x.No == item.OBJID).FirstAsync();
if (department == null) if (department == null)
{ {
department = new DepartmentEntity department = new DepartmentEntity
@@ -47,14 +44,14 @@ public class SyncOrganizationJob
PNo = item.SOBID, PNo = item.SOBID,
}; };
await _dbContext.InsertAsync(department); await DbContext.InsertAsync(department);
} }
else else
{ {
department.PNo = item.SOBID; department.PNo = item.SOBID;
department.Name = item.STEXT; department.Name = item.STEXT;
await _dbContext.UpdateAsync(department); await DbContext.UpdateAsync(department);
} }
} }
} }

View File

@@ -0,0 +1,74 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Dlhis.ScheduledTask.SyncSapInfo;
using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using Kurisu.SqlSugar.Services;
using SqlSugar;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 上传考勤记录
/// </summary>
[RecurringJob(nameof(SyncOriginCardInfoJob), "0/20 * * * * ?", Name = "上传考勤记录", Queue = "SAP")]
public class SyncOriginCardInfoJob : BaseJob
{
private readonly IJinkoJIPService _jinkoJIPService;
private readonly ISqlSugarOptionsService _sqlSugarOptionsService;
public SyncOriginCardInfoJob(IJinkoJIPService jinkoJIPService, IServiceProvider serviceProvider) : base(serviceProvider)
{
_jinkoJIPService = jinkoJIPService;
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
//考勤设备(不需要Enable参数)
var devices = await DbContext.Queryable<DeviceEntity>()
.Where(x => x.Type == DeviceTypeEnumType.Attendance || x.Type == DeviceTypeEnumType.Door && x.AsAttendance)
.ToListAsync();
//获取上传日志(有效的)
var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList();
var logs = await DbContext.Queryable<DeviceUploadLogEntity>()
.Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo))
.Where(x => !string.IsNullOrEmpty(x.UserUniqueNo))
.Where(x => x.Sync == false)
.ToListAsync();
//获取人员信息
var userCodes = logs.Select(x => x.UserUniqueNo).ToList();
var gainfoCodes = await DbContext.Queryable<GainfoEntity>().Where(x => userCodes.Contains(x.Code)).Select(x => x.Code).ToListAsync();
var requests = logs.Select(x => new UploadAttendanceRequest
{
Id = x.Id,
RaiseDateTime = x.RaiseDateTime,
ZNOTE = devices.First(item => item.UniqueNo == x.DeviceUniqueNo).DeployPlace,
ZWORKCODE = gainfoCodes.Contains(x.UserUniqueNo) ? x.UserUniqueNo?.TrimEnd('\0') : string.Empty,
}).Where(x => x.ZWORKCODE.IsPresent()).ToList();
var ids = requests.Select(x => x.Id).ToList();
if (ids.Any())
{
var r = await _jinkoJIPService.UploadAttendancesAsync(requests);
if (r.OLX == "0")
{
await DbContext.Updateable<DeviceUploadLogEntity>()
.SetColumns(x => x.Sync == true)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => ids.Contains(x.Id))
.ExecuteCommandAsync();
}
else
{
throw new Exception(r.OMS);
}
}
}
}

View File

@@ -1,156 +1,147 @@
using System.Data; using System.Data;
using System.Text; using System.Text;
using Kurisu.SqlSugar.Services;
using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Entity;
using Dlhis.ScheduledTask.SyncSapInfo.Dto; using Dlhis.ScheduledTask.SyncSapInfo.Dto;
using Dlhis.ScheduledTask.SyncSapInfo.Comparers; using Dlhis.ScheduledTask.SyncSapInfo.Comparers;
using Dlhis.ScheduledTask.SyncSapInfo.Services; using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Kurisu.AspNetCore.Scope;
using Dlhis.Entity.Basic.Enums; using Dlhis.Entity.Basic.Enums;
using Dlhis.Sys.Api; using Dlhis.Sys.Api;
using Dlhis.Sys.Api.Response; using Dlhis.Sys.Api.Response;
using SqlSugar; using SqlSugar;
using Dlhis.Basic.Api; using Dlhis.Basic.Api;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
namespace Dlhis.ScheduledTask.SyncSapInfo.Job; namespace Dlhis.ScheduledTask.Jobs;
public class SyncPersonJob /// <summary>
/// SAP人员信息同步
/// </summary>
[RecurringJob(nameof(SyncPersonJob), "0 0 1-4 * * ?", Name = "SAP人员信息同步", Queue = "SAP")]
public class SyncPersonJob : BaseJob
{ {
private readonly ILogger _logger;
private readonly IJinkoSapService _jinkoSapService; private readonly IJinkoSapService _jinkoSapService;
private readonly ITenantApi _tenantApi; private readonly ITenantApi _tenantApi;
private readonly IDevicePermissionApi _devicePermissionApi; private readonly IDevicePermissionApi _devicePermissionApi;
private List<PositionEntity> _positions;
private List<TenantResponse> _tenants; private List<TenantResponse> _tenants;
private List<SapPersonDepartmentStatus> _departmentChanges; private List<SapPersonDepartmentStatus> _departmentChanges;
private List<SapPersonJobStatus> _jobChanges; private List<SapPersonJobStatus> _jobChanges;
public SyncPersonJob(ILogger<SyncPersonJob> logger, IJinkoSapService jinkoSapService, public SyncPersonJob(
ITenantApi tenantApi, IDevicePermissionApi devicePermissionApi) IJinkoSapService jinkoSapService,
ITenantApi tenantApi,
IServiceProvider serviceProvider,
IDevicePermissionApi devicePermissionApi) : base(serviceProvider)
{ {
_logger = logger;
_jinkoSapService = jinkoSapService; _jinkoSapService = jinkoSapService;
_tenantApi = tenantApi; _tenantApi = tenantApi;
_devicePermissionApi = devicePermissionApi; _devicePermissionApi = devicePermissionApi;
} }
public async Task RunAsync() public override async Task RunAsync(IServiceProvider serviceProvider)
{ {
_logger.LogInformation("开始获取SAP人员"); Logger.LogWarning("开始获取SAP人员");
DbContext.Client.QueryFilter.ClearAndBackup();
var personResp = await _jinkoSapService.GetPersonsAsync(); var personResp = await _jinkoSapService.GetPersonsAsync();
var persons = personResp.Person.Items; var persons = personResp.Person.Items;
_departmentChanges = personResp.DepartmentInfo.Items; _departmentChanges = personResp.DepartmentInfo.Items;
_jobChanges = personResp.JobStatusInfo.Items; _jobChanges = personResp.JobStatusInfo.Items;
_logger.LogInformation("获取SAP人员数量为:{count}", persons.Count); Logger.LogWarning("获取SAP人员数量为:{count}", persons.Count);
if (persons.Count > 0) if (persons.Count > 0)
{ {
Scoped.Temp.Value.Create(sp => var modifiedPersons = GetModifiedPerson(persons.OrderByDescending(x => x.ZSTATUS).ThenBy(x => x.ZRZRQ).ToList());
Logger.LogWarning("SAP人员差异数量为:{count}", modifiedPersons.Count);
if (modifiedPersons.Count > 0)
{ {
var dbContext = sp.GetService<IDbContext>(); await SavePersonAsync(modifiedPersons);
_positions = dbContext.Queryable<PositionEntity>().ToList(); }
var modifiedPersons = GetModifiedPerson(dbContext, persons.OrderByDescending(x => x.ZSTATUS).ThenBy(x => x.ZRZRQ).ToList());
_logger.LogInformation("SAP人员差异数量为:{count}", modifiedPersons.Count);
if (modifiedPersons.Count > 0)
{
var failedRecords = SavePerson(modifiedPersons);
}
});
} }
_logger.LogInformation("同步SAP人员数据结束"); Logger.LogWarning("同步SAP人员数据结束");
} }
private async Task<int> SavePerson(List<SapPersonItem> persons) private async Task SavePersonAsync(List<SapPersonItem> persons)
{ {
var tenantResult = await _tenantApi.GetTenantsInternalAsync(); var tenantResult = await _tenantApi.GetTenantsInternalAsync();
_tenants = tenantResult.Data; _tenants = tenantResult.Data;
var failure = 0;
var workCodes = persons.Select(x => x.ZWORKCODE).ToList(); var workCodes = persons.Select(x => x.ZWORKCODE).ToList();
var chunks = workCodes.Chunk(4000).ToList(); var chunks = workCodes.Chunk(4000).ToList();
var dtoResult = await _devicePermissionApi.GetPermissionUsersInfoAsync(); var dtoResult = await _devicePermissionApi.GetPermissionUsersInfoAsync();
var dto = dtoResult.Data; var dto = dtoResult.Data;
Scoped.Temp.Value.Create(sp => foreach (var searchCodes in chunks)
{ {
var dbContext = sp.GetService<IDbContext>(); Logger.LogWarning("SAP分批处理Chunk数量:{count}", searchCodes.Length);
var existsWorkCodes = DbContext.Queryable<GainfoSapEntity>()
.Where(x => searchCodes.Contains(x.ename))
.Select(x => x.ename)
.ToList();
foreach (var searchCodes in chunks) var originUpdates = persons.Where(x => existsWorkCodes.Contains(x.ZWORKCODE)).ToList();
var existsUpdateWorkCodes = originUpdates.Select(x => x.ZWORKCODE).ToList();
var originInserts = persons.Where(x => searchCodes.Contains(x.ZWORKCODE)).Where(x => !existsUpdateWorkCodes.Contains(x.ZWORKCODE)).ToList();
int i = 0;
var updates = ToGainfoSap(originUpdates);
Logger.LogWarning("SAP分批更新数:{count}", updates.Count);
var inserts = ToGainfoSap(originInserts);
Logger.LogWarning("SAP分批新增数:{count}", inserts.Count);
foreach (var item in inserts)
{ {
_logger.LogInformation("SAP分批处理Chunk数量:{count}", searchCodes.Length); i++;
var existsWorkCodes = dbContext.Queryable<GainfoSapEntity>() try
.Where(x => searchCodes.Contains(x.ename))
.Select(x => x.ename)
.ToList();
var originUpdates = persons.Where(x => existsWorkCodes.Contains(x.ZWORKCODE)).ToList();
var existsUpdateWorkCodes = originUpdates.Select(x => x.ZWORKCODE).ToList();
var originInserts = persons.Where(x => searchCodes.Contains(x.ZWORKCODE)).Where(x => !existsUpdateWorkCodes.Contains(x.ZWORKCODE)).ToList();
int i = 0;
var currentTotal = originUpdates.Count + originInserts.Count;
var updates = ToGainfoSap(originUpdates);
_logger.LogInformation("SAP分批更新数:{count}", updates.Count);
var inserts = ToGainfoSap(originInserts);
_logger.LogInformation("SAP分批新增数:{count}", inserts.Count);
foreach (var item in inserts)
{ {
i++; InsertPerson(item);
try Logger.LogWarning("执行新增成功 工号:{code},姓名:{name}", item.ename, item.gname);
{
InsertPerson(dbContext, item);
_logger.LogInformation("执行新增成功 工号:{code},姓名:{name}", item.ename, item.gname);
}
catch (Exception ex)
{
failure++;
_logger.LogError(ex, "执行新增失败:{message}", ex.Message);
}
} }
catch (Exception ex)
foreach (var item in updates)
{ {
i++; Logger.LogError(ex, "执行新增失败:{message}", ex.Message);
try
{
UpdatePerson(dbContext, item);
_logger.LogInformation("执行更新成功 工号:{code},姓名:{name}", item.ename, item.gname);
}
catch (Exception ex)
{
failure++;
_logger.LogError(ex, "执行更新失败:{message}", ex.Message);
}
} }
} }
});
foreach (var item in updates)
{
i++;
try
{
UpdatePerson(item);
Logger.LogWarning("执行更新成功 工号:{code},姓名:{name}", item.ename, item.gname);
}
catch (Exception ex)
{
Logger.LogError(ex, "执行更新失败:{message}", ex.Message);
}
}
}
var rResult = await _devicePermissionApi.RePermissionUsersInfoAsync(dto); var rResult = await _devicePermissionApi.RePermissionUsersInfoAsync(dto);
var r = rResult.Data; var r = rResult.Data;
_logger.LogInformation("生成下发任务数量:{count}", r);
return 0; Logger.LogWarning("生成下发任务数量:{count}", r);
} }
private void InsertPerson(IDbContext db, GainfoSapEntity item) private void InsertPerson(GainfoSapEntity item)
{ {
db.Client.Insertable(item).ExecuteCommand(); DbContext.Client.Insertable(item).ExecuteCommand();
var info = GainfoSapToGainfo(item); var info = GainfoSapToGainfo(item);
if (item.isowner == "T") if (item.isowner == "T")
{ {
db.Client.Insertable(info).ExecuteCommand(); DbContext.Client.Insertable(info).ExecuteCommand();
} }
else else
{ {
db.Client.Insertable(info).AS("GainfoLeave").ExecuteCommand(); DbContext.Client.Insertable(info).AS("GainfoLeave").ExecuteCommand();
} }
} }
@@ -187,10 +178,10 @@ public class SyncPersonJob
return info; return info;
} }
private void UpdatePerson(IDbContext db, GainfoSapEntity item) private void UpdatePerson(GainfoSapEntity item)
{ {
var gainfoSap = db.Queryable<GainfoSapEntity>().Where(x => x.ename == item.ename).First(); var gainfoSap = DbContext.Queryable<GainfoSapEntity>().Where(x => x.ename == item.ename).First();
db.Updateable<GainfoSapEntity>().SetColumns(g => new GainfoSapEntity DbContext.Updateable<GainfoSapEntity>().SetColumns(g => new GainfoSapEntity
{ {
PERNR = item.PERNR, PERNR = item.PERNR,
gname = item.gname, gname = item.gname,
@@ -211,14 +202,14 @@ public class SyncPersonJob
.Where(x => x.ename == item.ename) .Where(x => x.ename == item.ename)
.ExecuteCommand(); .ExecuteCommand();
var gainfo = db.Queryable<GainfoEntity>().Where(x => x.Code == item.ename).First(); var gainfo = DbContext.Queryable<GainfoEntity>().Where(x => x.Code == item.ename).First();
if (item.isowner == "T") if (item.isowner == "T")
{ {
if (gainfo != null) if (gainfo != null)
{ {
var workplace = _tenants.FirstOrDefault(x => x.Name == item.ZZ_GZDJDNM)?.Code ?? item.ZZ_GZDJDNM; var workplace = _tenants.FirstOrDefault(x => x.Name == item.ZZ_GZDJDNM)?.Code ?? item.ZZ_GZDJDNM;
var factory = _tenants.FirstOrDefault(x => x.Name == item.ZZ_GZDCQNM)?.Code ?? item.ZZ_GZDCQNM; var factory = _tenants.FirstOrDefault(x => x.Name == item.ZZ_GZDCQNM)?.Code ?? item.ZZ_GZDCQNM;
db.Updateable<GainfoEntity>().SetColumns(g => new GainfoEntity DbContext.Updateable<GainfoEntity>().SetColumns(g => new GainfoEntity
{ {
SapNo = item.PERNR, SapNo = item.PERNR,
Name = item.gname, Name = item.gname,
@@ -240,23 +231,23 @@ public class SyncPersonJob
else else
{ {
var info = GainfoSapToGainfo(item); var info = GainfoSapToGainfo(item);
db.Client.Insertable(info).ExecuteCommand(); DbContext.Client.Insertable(info).ExecuteCommand();
} }
} }
else else
{ {
if (gainfo != null) if (gainfo != null)
{ {
db.Client.Deleteable(gainfo).ExecuteCommand(); DbContext.Client.Deleteable(gainfo).ExecuteCommand();
gainfo.Id = 0; gainfo.Id = 0;
db.Client.Insertable(gainfo).AS("GainfoLeave").ExecuteCommand(); DbContext.Client.Insertable(gainfo).AS("GainfoLeave").ExecuteCommand();
} }
} }
} }
private List<SapPersonItem> GetModifiedPerson(IDbContext db, List<SapPersonItem> sapList) private List<SapPersonItem> GetModifiedPerson(List<SapPersonItem> sapList)
{ {
var last = LoadData(db); var last = LoadData();
if (last.Count <= 0) if (last.Count <= 0)
{ {
return sapList; return sapList;
@@ -294,9 +285,9 @@ public class SyncPersonJob
} }
private static List<SapPersonItem> LoadData(IDbContext db) private List<SapPersonItem> LoadData()
{ {
var gainfo = db.Queryable<GainfoSapEntity>().ToList(); var gainfo = DbContext.Queryable<GainfoSapEntity>().ToList();
return gainfo.Select(x => new SapPersonItem return gainfo.Select(x => new SapPersonItem
{ {
@@ -335,7 +326,7 @@ public class SyncPersonJob
GainfoSapEntity o = null; GainfoSapEntity o = null;
try try
{ {
var position = _positions.FirstOrDefault(x => x.No == person.PLANS); //var position = _positions.FirstOrDefault(x => x.No == person.PLANS);
var rzrq = SafeParseDate(person.ZRZRQ); var rzrq = SafeParseDate(person.ZRZRQ);
var lzrq = SafeParseDate(person.ZLZRQ); var lzrq = SafeParseDate(person.ZLZRQ);
@@ -378,7 +369,7 @@ public class SyncPersonJob
remark = rmk, remark = rmk,
native = native, native = native,
sex = sex, sex = sex,
web = SubString(position?.Name ?? "", 30), //web = SubString(position?.Name ?? "", 30),
logdate = DateTime.Now, logdate = DateTime.Now,
isowner = int.Parse(person.ZSTATUS) < 5 ? "T" : "F", isowner = int.Parse(person.ZSTATUS) < 5 ? "T" : "F",
@class = "C", @class = "C",
@@ -409,7 +400,7 @@ public class SyncPersonJob
{ {
if (string.IsNullOrWhiteSpace(idno) || idno.Length < 6) return null; if (string.IsNullOrWhiteSpace(idno) || idno.Length < 6) return null;
idno = idno.TrimStart(); idno = idno.TrimStart();
return idno.Substring(0, 6); return idno[..6];
} }
private static DateTime? SafeParseDate(string str) private static DateTime? SafeParseDate(string str)

View File

@@ -1,33 +1,31 @@
using Dlhis.Entity.Basic.Entity; using Dlhis.Entity.Basic.Entity;
using Dlhis.ScheduledTask.SyncSapInfo.Dto; using Dlhis.ScheduledTask.SyncSapInfo.Dto;
using Dlhis.ScheduledTask.SyncSapInfo.Services; using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Kurisu.SqlSugar.Services; using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
namespace Dlhis.ScheduledTask.SyncSapInfo.Job; namespace Dlhis.ScheduledTask.Jobs;
/// <summary> /// <summary>
/// 职位同步定时任务 /// SAP岗位信息同步
/// </summary> /// </summary>
public class SyncPositionJob [RecurringJob(nameof(SyncPositionJob), "0 0 0 * * ?", Name = "SAP岗位信息同步", Queue = "SAP")]
public class SyncPositionJob : BaseJob
{ {
private readonly IDbContext _dbContext;
private readonly IJinkoSapService _jinkoSapService; private readonly IJinkoSapService _jinkoSapService;
private readonly ILogger<SyncPositionJob> _logger;
public SyncPositionJob(IDbContext dbContext, IJinkoSapService jinkoSapService, ILogger<SyncPositionJob> logger) public SyncPositionJob(IServiceProvider serviceProvider, IJinkoSapService jinkoSapService) : base(serviceProvider)
{ {
_dbContext = dbContext;
_jinkoSapService = jinkoSapService; _jinkoSapService = jinkoSapService;
_logger = logger;
} }
public async Task RunAsync() public override async Task RunAsync(IServiceProvider serviceProvider)
{ {
var positionResult = await _jinkoSapService.GetPositionsAsync(); var positionResult = await _jinkoSapService.GetPositionsAsync();
if (positionResult.Position != null && positionResult.Position.Items.Count > 0) if (positionResult.Position != null && positionResult.Position.Items.Count > 0)
{ {
_logger.LogWarning("SAP岗位数量:{count}", positionResult.Position.Items.Count); Logger.LogWarning("SAP岗位数量:{count}", positionResult.Position.Items.Count);
await SaveOrg(positionResult.Position.Items); await SaveOrg(positionResult.Position.Items);
} }
} }
@@ -36,7 +34,7 @@ public class SyncPositionJob
{ {
foreach (var item in modifiedPostList) foreach (var item in modifiedPostList)
{ {
var position = await _dbContext.Queryable<PositionEntity>().Where(x => x.No == item.ZJOBID).FirstAsync(); var position = await DbContext.Queryable<PositionEntity>().Where(x => x.No == item.ZJOBID).FirstAsync();
if (position == null) if (position == null)
{ {
position = new PositionEntity position = new PositionEntity
@@ -47,7 +45,7 @@ public class SyncPositionJob
Grade = item.ZZWID Grade = item.ZZWID
}; };
await _dbContext.InsertAsync(position); await DbContext.InsertAsync(position);
} }
else else
{ {
@@ -55,7 +53,7 @@ public class SyncPositionJob
position.DepartmentNo = item.ZDEPID; position.DepartmentNo = item.ZDEPID;
position.Grade = item.ZZWID; position.Grade = item.ZZWID;
await _dbContext.UpdateAsync(position); await DbContext.UpdateAsync(position);
} }
} }
} }

View File

@@ -0,0 +1,111 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Sys.Entities;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using Kurisu.SqlSugar.Services;
using Microsoft.AspNetCore.Identity;
namespace Dlhis.ScheduledTask.Jobs;
/// <summary>
/// 用户创建
/// </summary>
[RecurringJob(nameof(UpdateUserPasswordJob), "0 0 6 * * ?", Name = "用户创建")]
public class UpdateUserPasswordJob : BaseJob
{
public UpdateUserPasswordJob(IServiceProvider serviceProvider) : base(serviceProvider)
{
}
public override async Task RunAsync(IServiceProvider serviceProvider)
{
var identityDbContext = DbContext.ChangeDb("identity");
var whitelist = new List<string>() { "admin", "WB-0741", "WB-1091", "0123456" };
var users = await identityDbContext.Queryable<UserEntity>()
.Where(x => !whitelist.Contains(x.UserName))
.ToListAsync();
var gainfos = await DbContext.Queryable<GainfoEntity>().Where(x => !whitelist.Contains(x.Code)).ToListAsync();
var hasher = new PasswordHasher<UserEntity>();
foreach (var gainfo in gainfos)
{
if (gainfo.IdNo.IsPresent())
{
try
{
var password = gainfo.IdNo.TakeLast(6).Join("");
var user = users.FirstOrDefault(x => x.UserName == gainfo.Code);
if (user == null)
{
user = new UserEntity
{
UserName = gainfo.Code,
NormalizedUserName = gainfo.Code.ToUpper(),
ConcurrencyStamp = Guid.NewGuid().ToString(),
SecurityStamp = Guid.NewGuid().ToString(),
};
password = hasher.HashPassword(user, password);
user.PasswordHash = password;
var id = (int)await identityDbContext.InsertReturnIdentityAsync(user);
await AddUserClaim(identityDbContext, id, "jinko-code", gainfo.Code);
await AddUserClaim(identityDbContext, id, "name", gainfo.Name);
if (!string.IsNullOrEmpty(gainfo.Factory))
{
var userTenant = await identityDbContext.Queryable<UserTenantEntity>().Where(x => x.UserId == user.Id).FirstAsync();
if (userTenant == null)
{
userTenant = new UserTenantEntity
{
UserId = user.Id,
TenantCode = gainfo.Factory
};
await identityDbContext.InsertAsync(userTenant);
}
else
{
userTenant.TenantCode = gainfo.Factory;
await identityDbContext.UpdateAsync(userTenant);
}
}
}
else
{
password = hasher.HashPassword(user, password);
user.PasswordHash = password;
await identityDbContext.UpdateAsync(user);
}
}
catch (Exception)
{
}
}
}
}
private async Task AddUserClaim(IDbContext dbContext, int userId, string type, string value)
{
var claim = await dbContext.Queryable<UserClaimEntity>().Where(x => x.UserId == userId).FirstAsync(x => x.ClaimType == type);
if (claim == null)
{
claim = new UserClaimEntity()
{
UserId = userId,
ClaimType = type,
ClaimValue = value
};
await dbContext.InsertAsync(claim);
}
else
{
claim.ClaimValue = value;
await dbContext.UpdateAsync(claim);
}
}
}

View File

@@ -22,7 +22,8 @@ public class HangfirePack : BaseAppPack
{ {
Db = 5, Db = 5,
Prefix = "hangfire" Prefix = "hangfire"
}); })
.UseSerilogLogProvider();
}); });

View File

@@ -1,8 +1,12 @@
using Dlhis.ScheduledTask.CommonJobs; using System.Linq.Expressions;
using Dlhis.ScheduledTask.SyncSapInfo.Job; using System.Reflection;
using Dlhis.ScheduledTask.TaskAssignment.Job;
using Hangfire; using Hangfire;
using Hangfire.Common;
using Kurisu.AspNetCore.Startup; using Kurisu.AspNetCore.Startup;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Scheduled;
using Kurisu.Scheduled.Attributes;
using SqlSugar;
namespace Dlhis.ScheduledTask.Packs; namespace Dlhis.ScheduledTask.Packs;
@@ -13,68 +17,34 @@ public class JobPack : BaseAppPack
public override void Invoke(IServiceProvider serviceProvider) public override void Invoke(IServiceProvider serviceProvider)
{ {
RecurringJob.AddOrUpdate<SyncOrganizationJob>("SyncSap", s => s.RunAsync(), "0 0 2 * * ?", new RecurringJobOptions var logger = serviceProvider.GetService<ILogger<JobPack>>();
{
TimeZone = TimeZoneInfo.Local
});
RecurringJob.AddOrUpdate<SyncPersonJob>("SyncPerson", s => s.RunAsync(), "0 0 3 * * ?", new RecurringJobOptions var jobs = Assembly.GetEntryAssembly().GetTypes()
{ .Where(x => x.IsAssignableTo(typeof(BaseJob)) && x != typeof(BaseJob))
TimeZone = TimeZoneInfo.Local .Where(x => x.IsDefined(typeof(RecurringJobAttribute)))
}); .ToList();
//创建设备同步时间任务 var setJob = typeof(Job).GetMethod(nameof(Job.FromExpression), BindingFlags.NonPublic | BindingFlags.Static, new[] { typeof(LambdaExpression), typeof(Type), typeof(string) });
RecurringJob.AddOrUpdate<CreateSyncServerDateTimeJob>("SyncServerDateTime", s => s.RunAsync(), "0 0 0/1 * * ?", new RecurringJobOptions var m = (Lazy<RecurringJobManager>)typeof(RecurringJob).GetField("Instance", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
{
TimeZone = TimeZoneInfo.Local
});
//创建设备获取的任务 (jobs.Select(job => job.GetCustomAttribute<RecurringJobAttribute>().UniqueJobKey).Distinct().Count() != jobs.Count).ThrowIfTrue("任务key重复");
RecurringJob.AddOrUpdate<CreateGetUploadRecordJob>("CreateGetUploadRecord", s => s.RunAsync(), "0 0/1 * * * ?", new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Local
});
//创建系统默认分组 logger.LogDebug("一共获取{count}个调度任务", jobs.Count);
RecurringJob.AddOrUpdate<CreateDefinitelyGroupJob>("CreateDefinitelyGroup", s => s.RunAsync(), "0 0 5 * * ?", new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Local
});
//心跳 foreach (var job in jobs)
RecurringJob.AddOrUpdate<HeartbeatJob>("Heartbeat", s => s.RunAsync(), "0/10 * * * * ?", new RecurringJobOptions
{ {
TimeZone = TimeZoneInfo.Local var recurringSetting = job.GetCustomAttribute<RecurringJobAttribute>()!;
}); recurringSetting.Cron.IsEmpty().ThrowIfTrue(job.Name + "尚未定义Cron");
//上传考勤记录 var parameterExpression = Expression.Parameter(job);
RecurringJob.AddOrUpdate<SyncOriginCardInfoJob>("SyncOriginCardInfo", s => s.RunAsync(), "0/20 * * * * ?", new RecurringJobOptions var todo = job.GetMethod(nameof(BaseJob.TodoAsync));
{ var todoExp = Expression.Call(parameterExpression, todo);
TimeZone = TimeZoneInfo.Local
});
//处理重复下发任务 var nJob = (Job)setJob.Invoke(null, new object[] { Expression.Lambda(todoExp), job, "default" });
RecurringJob.AddOrUpdate<CancelDuplicationTaskJob>("CancelDuplicationTask", s => s.RunAsync(), "0/15 * * * * ?", new RecurringJobOptions m.Value.AddOrUpdate(recurringSetting.UniqueJobKey, nJob, recurringSetting.Cron, new RecurringJobOptions
{ {
TimeZone = TimeZoneInfo.Local TimeZone = TimeZoneInfo.Local
}); });
}
//更新用户密码
RecurringJob.AddOrUpdate<UpdateUserPasswordJob>("UpdateUserPassword", s => s.RunAsync(), "0 0 5 * * ?", new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Local
});
//删除前三天前的日志
RecurringJob.AddOrUpdate<DelApiLogTaskJob>("DelApiLogTask", s => s.RunAsync(), "0 0 */7 * *", new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Local
});
//处理餐补
RecurringJob.AddOrUpdate<MealSupplementHandlerJob>("MealSupplementHandler", s => s.RunAsync(), "0 0 0/1 * * ?", new RecurringJobOptions
{
TimeZone = TimeZoneInfo.Local
});
} }
} }

View File

@@ -1,95 +0,0 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Dlhis.ScheduledTask.SyncSapInfo.Services;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
using SqlSugar;
namespace Dlhis.ScheduledTask.SyncSapInfo.Job;
public class SyncOriginCardInfoJob
{
private readonly IJinkoJIPService _jinkoJIPService;
private readonly IDbContext _dbContext;
private readonly ISqlSugarOptionsService _sqlSugarOptionsService;
private readonly ILogger<SyncOriginCardInfoJob> _logger;
public SyncOriginCardInfoJob(IJinkoJIPService jinkoJIPService,
IDbContext dbContext,
ISqlSugarOptionsService sqlSugarOptionsService,
ILogger<SyncOriginCardInfoJob> logger
)
{
_jinkoJIPService = jinkoJIPService;
_dbContext = dbContext;
_sqlSugarOptionsService = sqlSugarOptionsService;
_logger = logger;
}
public async Task RunAsync()
{
_logger.LogInformation("调度任务:{job}.开始执行", nameof(SyncOriginCardInfoJob));
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
_sqlSugarOptionsService.IgnoreTenant = true;
try
{
//考勤设备(不需要Enable参数)
var devices = await _dbContext.Queryable<DeviceEntity>()
.Where(x => x.Type == DeviceTypeEnumType.Attendance || (x.Type == DeviceTypeEnumType.Door && x.AsAttendance))
.ToListAsync();
//获取上传日志(有效的)
var deviceUniqueNos = devices.Select(x => x.UniqueNo).ToList();
var logs = await _dbContext.Queryable<DeviceUploadLogEntity>()
.Where(x => deviceUniqueNos.Contains(x.DeviceUniqueNo))
.Where(x => !string.IsNullOrEmpty(x.UserUniqueNo))
.Where(x => x.Sync == false)
.ToListAsync();
//获取人员信息
var userCodes = logs.Select(x => x.UserUniqueNo).ToList();
var gainfoCodes = await _dbContext.Queryable<GainfoEntity>().Where(x => userCodes.Contains(x.Code)).Select(x => x.Code).ToListAsync();
var requests = logs.Select(x => new UploadAttendanceRequest
{
Id = x.Id,
RaiseDateTime = x.RaiseDateTime,
ZNOTE = devices.First(item => item.UniqueNo == x.DeviceUniqueNo).DeployPlace,
ZWORKCODE = gainfoCodes.Contains(x.UserUniqueNo) ? x.UserUniqueNo?.TrimEnd('\0') : string.Empty,
}).Where(x => x.ZWORKCODE.IsPresent()).ToList();
var ids = requests.Select(x => x.Id).ToList();
if (ids.Any())
{
var r = await _jinkoJIPService.UploadAttendancesAsync(requests);
if (r.OLX == "0")
{
await _dbContext.Updateable<DeviceUploadLogEntity>()
.SetColumns(x => x.Sync == true)
.SetColumns(x => x.ModifiedTime == DateTime.Now)
.Where(x => ids.Contains(x.Id))
.ExecuteCommandAsync();
}
else
{
throw new Exception(r.OMS);
}
}
}
catch (Exception ex)
{
_logger.LogError("上传考勤记录失败:{message}", ex.Message);
}
finally
{
_sqlSugarOptionsService.IgnoreTenant = false;
_dbContext.Client.QueryFilter.Restore();
_logger.LogInformation("调度任务:{job}.结束", nameof(SyncOriginCardInfoJob));
}
}
}

View File

@@ -60,7 +60,7 @@ public class JinkoJIPService : IJinkoJIPService
{ {
var requestUrl = "/JIP/DHR/DHR_DL_CALLBACK_UAT"; var requestUrl = "/JIP/DHR/DHR_DL_CALLBACK_UAT";
var json = requests.ToJson(); var json = requests.ToJson(Kurisu.AspNetCore.Utils.Extensions.JsonExtensions.DefaultSetting);
var content = new StringContent(json, Encoding.UTF8, "application/json"); var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await GetHttpClient().PostAsync(requestUrl, content); var response = await GetHttpClient().PostAsync(requestUrl, content);

View File

@@ -112,10 +112,12 @@ public class JinkoSapService : IJinkoSapService
{ {
departmentInfos.AddRange(personResp.DepartmentInfo.Items); departmentInfos.AddRange(personResp.DepartmentInfo.Items);
} }
_logger.LogInformation("获取人事范围{werk}完成", item.Werk);
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "获取人事范围{werk}失败:{message}", ex.Message, item.Werk); _logger.LogError(ex, "获取人事范围{werk}失败:{message}", item.Werk, ex.Message);
} }
} }

View File

@@ -1,70 +0,0 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Kurisu.AspNetCore.Utils.Extensions;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
namespace Dlhis.ScheduledTask.TaskAssignment.Job;
public class CreateGetUploadRecordJob
{
private readonly IDbContext _dbContext;
private readonly ILogger<CreateGetUploadRecordJob> _logger;
private readonly ISqlSugarOptionsService _sqlSugarOptionsService;
public CreateGetUploadRecordJob(IDbContext dbContext, ILogger<CreateGetUploadRecordJob> logger, ISqlSugarOptionsService sqlSugarOptionsService)
{
_logger = logger;
_sqlSugarOptionsService = sqlSugarOptionsService;
_dbContext = dbContext;
}
public async Task RunAsync()
{
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
_sqlSugarOptionsService.IgnoreTenant = true;
try
{
var devices = await _dbContext.Queryable<DeviceEntity>()
.Where(x => x.Enable)
.Where(x => x.Type == DeviceTypeEnumType.Attendance || x.Type == DeviceTypeEnumType.Door)
.ToListAsync();
//过滤处理中的任务
var currentUniqueNos = await _dbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.Type == DeviceTaskEnumType.UploadRecord)
.Where(x => x.Status == DeviceTaskStatusEnumType.Process || x.Status == DeviceTaskStatusEnumType.Wait)
.Select(x => x.DeviceUniqueNo)
.ToListAsync();
var tasks = devices.Where(x => !currentUniqueNos.Contains(x.UniqueNo)).Select(x => new DeviceTaskEntity
{
DeviceUniqueNo = x.UniqueNo,
Status = DeviceTaskStatusEnumType.Wait,
TenantId = x.TenantId,
Version = 0,
Type = DeviceTaskEnumType.UploadRecord,
CreatedBy = 1,
CreateTime = DateTime.Now,
ModifiedBy = 1,
ModifiedTime = DateTime.Now,
})
.ToList();
await _dbContext.InsertAsync(tasks);
}
catch (Exception ex)
{
_logger.LogError(ex, "生成{type}任务失败:{message}", DeviceTaskEnumType.UploadRecord.GetDescription(), ex.Message);
}
finally
{
_sqlSugarOptionsService.IgnoreTenant = false;
_dbContext.Client.QueryFilter.Restore();
}
}
}

View File

@@ -1,71 +0,0 @@
using Dlhis.Entity.Basic.Entity;
using Dlhis.Entity.Device.Entities;
using Dlhis.Entity.Device.Enums;
using Kurisu.Core.DataAccess.Entity;
using Kurisu.SqlSugar.Services;
namespace Dlhis.ScheduledTask.TaskAssignment.Job
{
public class CreateSyncServerDateTimeJob
{
private readonly IDbContext _dbContext;
private readonly ILogger<CreateSyncServerDateTimeJob> _logger;
private readonly ISqlSugarOptionsService _sqlSugarOptionsService;
public CreateSyncServerDateTimeJob(IDbContext dbContext, ILogger<CreateSyncServerDateTimeJob> logger, ISqlSugarOptionsService sqlSugarOptionsService)
{
_dbContext = dbContext;
_logger = logger;
_sqlSugarOptionsService = sqlSugarOptionsService;
}
public async Task RunAsync()
{
_dbContext.Client.QueryFilter.ClearAndBackup<ITenantId>();
_sqlSugarOptionsService.IgnoreTenant = true;
try
{
var deviceUniqueNos = await _dbContext.Queryable<DeviceTaskEntity>()
.Where(x => x.Status == DeviceTaskStatusEnumType.Wait || x.Status == DeviceTaskStatusEnumType.Process)
.Where(x => x.Type == DeviceTaskEnumType.SyncDateTime)
.Select(x => x.DeviceUniqueNo)
.Distinct()
.ToListAsync();
var devices = await _dbContext.Queryable<DeviceEntity>()
.Where(x => !deviceUniqueNos.Contains(x.UniqueNo))
.Where(x => x.Enable)
.ToListAsync();
var tasks = devices.Select(x => new DeviceTaskEntity
{
DeviceUniqueNo = x.UniqueNo,
Status = DeviceTaskStatusEnumType.Wait,
TenantId = x.TenantId,
Version = 0,
UserNo = string.Empty,
Type = DeviceTaskEnumType.SyncDateTime,
CreatedBy = 1,
CreateTime = DateTime.Now,
ModifiedBy = 1,
ModifiedTime = DateTime.Now,
}).ToList();
await _dbContext.InsertAsync(tasks);
}
catch (Exception ex)
{
_logger.LogError(ex, "生成同步时间任务失败:{message}", ex.Message);
}
finally
{
_sqlSugarOptionsService.IgnoreTenant = false;
_dbContext.Client.QueryFilter.Restore();
}
}
}
}

View File

@@ -80,7 +80,6 @@ public class DeviceTaskAssignmentService : IDeviceTaskAssignmentService
{ {
var tasks = await _dbContext.Queryable<DeviceTaskEntity>() var tasks = await _dbContext.Queryable<DeviceTaskEntity>()
.Where(x => request.Ids.Contains(x.Id)) .Where(x => request.Ids.Contains(x.Id))
.Where(x => x.Status == DeviceTaskStatusEnumType.Process || x.Status == DeviceTaskStatusEnumType.Error)
.ToListAsync(); .ToListAsync();
//判断process任务时间 //判断process任务时间

View File

@@ -52,9 +52,9 @@
}, },
"JinkoOptions": { "JinkoOptions": {
"Jip": { "Jip": {
"BaseUrl": "http://172.16.202.34:8000", "BaseUrl": "http://jip.jinkosolar.com",
"Key": "FACE-7409WIBX", "Key": "FACE-CUOJG9N7",
"Secret": "be0a4b88-cd80-4509-b9b9-865205d0d3d8" "Secret": "130c2f2d-03a0-4720-8e25-538ffac7a7cb"
} }
}, },
"RedisOptions": { "RedisOptions": {
@@ -63,12 +63,12 @@
"AllowedHosts": "*", "AllowedHosts": "*",
"Serilog": { "Serilog": {
"MinimumLevel": { "MinimumLevel": {
"Default": "Information", "Default": "Warning",
"Override": { "Override": {
"System": "Warning", "System": "Warning",
"Microsoft": "Warning", "Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information", "Microsoft.Hosting.Lifetime": "Information",
"Hangfire": "Information" "Hangfire": "Warning"
} }
}, },
"WriteTo": [ "WriteTo": [