fix: 🐛 version lock

[skip ci]
This commit is contained in:
tk
2024-12-04 16:34:36 +08:00
committed by nsnail
parent 01058ba728
commit 4eecc0b4ce
9 changed files with 63 additions and 27 deletions

View File

@ -3,6 +3,7 @@ using Microsoft.Net.Http.Headers;
using NetAdmin.Application.Repositories;
using NetAdmin.Domain;
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.Dto.Dependency;
namespace NetAdmin.Application.Services;
@ -64,17 +65,19 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
/// <param name="excludeFields">排除的属性</param>
/// <param name="whereExp">查询表达式</param>
/// <param name="whereSql">查询sql</param>
/// <param name="ignoreVersion">是否忽略版本锁</param>
/// <returns>更新行数</returns>
protected Task<int> UpdateAsync( //
TEntity newValue //
, IEnumerable<string> includeFields //
, string[] excludeFields = null //
, List<string> includeFields = null //
, List<string> excludeFields = null //
, Expression<Func<TEntity, bool>> whereExp = null //
, string whereSql = null)
, string whereSql = null //
, bool ignoreVersion = false)
{
// 默认匹配主键
whereExp ??= a => a.Id.Equals(newValue.Id);
var update = BuildUpdate(newValue, includeFields, excludeFields).Where(whereExp).Where(whereSql);
var update = BuildUpdate(newValue, includeFields, excludeFields, ignoreVersion).Where(whereExp).Where(whereSql);
return update.ExecuteAffrowsAsync();
}
@ -87,17 +90,19 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
/// <param name="excludeFields">排除的属性</param>
/// <param name="whereExp">查询表达式</param>
/// <param name="whereSql">查询sql</param>
/// <param name="ignoreVersion">是否忽略版本锁</param>
/// <returns>更新后的实体列表</returns>
protected Task<List<TEntity>> UpdateReturnListAsync( //
TEntity newValue //
, IEnumerable<string> includeFields //
, string[] excludeFields = null //
, List<string> includeFields = null //
, List<string> excludeFields = null //
, Expression<Func<TEntity, bool>> whereExp = null //
, string whereSql = null)
, string whereSql = null //
, bool ignoreVersion = false)
{
// 默认匹配主键
whereExp ??= a => a.Id.Equals(newValue.Id);
return BuildUpdate(newValue, includeFields, excludeFields).Where(whereExp).Where(whereSql).ExecuteUpdatedAsync();
return BuildUpdate(newValue, includeFields, excludeFields, ignoreVersion).Where(whereExp).Where(whereSql).ExecuteUpdatedAsync();
}
#endif
@ -127,17 +132,22 @@ public abstract class RepositoryService<TEntity, TPrimary, TLogger>(BasicReposit
return new FileStreamResult(stream, Chars.FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM);
}
private IUpdate<TEntity> BuildUpdate( //
TEntity entity //
, IEnumerable<string> includeFields //
, string[] excludeFields = null)
private IUpdate<TEntity> BuildUpdate(TEntity entity, List<string> includeFields, List<string> excludeFields, bool ignoreVersion)
{
var updateExp = includeFields == null
? Rpo.UpdateDiy.SetSource(entity)
: Rpo.UpdateDiy.SetDto(includeFields!.ToDictionary(
x => x, x => typeof(TEntity).GetProperty(x, BindingFlags.Public | BindingFlags.Instance)!.GetValue(entity)));
if (excludeFields != null) {
updateExp = updateExp.IgnoreColumns(excludeFields);
IUpdate<TEntity> updateExp;
if (includeFields.NullOrEmpty()) {
updateExp = Rpo.UpdateDiy.SetSource(entity);
if (!excludeFields.NullOrEmpty()) {
updateExp = updateExp.IgnoreColumns(excludeFields.ToArray());
}
}
else {
updateExp = Rpo.UpdateDiy.SetDto(includeFields!.ToDictionary(
x => x
, x => typeof(TEntity).GetProperty(x, BindingFlags.Public | BindingFlags.Instance)!.GetValue(entity)));
if (!ignoreVersion && entity is IFieldVersion ver) {
updateExp = updateExp.Where($"{nameof(IFieldVersion.Version)} = {ver.Version}");
}
}
return updateExp;

View File

@ -277,13 +277,13 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
{
var ret1 = await UpdateAsync( // 运行中,运行时间超过超时设定;置为空闲状态
new Sys_Job { Status = JobStatues.Idle }, [nameof(Sys_Job.Status)], null
, a => a.Status == JobStatues.Running && a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB))
, a => a.Status == JobStatues.Running && a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB), null, true)
.ConfigureAwait(false);
var ret2 = await UpdateAsync( // 空闲中,下次执行时间在当前时间减去超时时间以前;将下次执行时间调整到现在
new Sys_Job { NextExecTime = DateTime.Now, NextTimeId = DateTime.Now.TimeUnixUtc() }
, [nameof(Sys_Job.NextExecTime), nameof(Sys_Job.NextTimeId)], null
, a => a.Status == JobStatues.Idle && a.NextExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB))
, [nameof(Sys_Job.NextExecTime), nameof(Sys_Job.NextTimeId)], null
, a => a.Status == JobStatues.Idle && a.NextExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB), null, true)
.ConfigureAwait(false);
return ret1 + ret2;
}

View File

@ -160,7 +160,7 @@ public sealed class UserProfileService(BasicRepository<Sys_UserProfile, long> rp
req.AppConfig = BuildAppConfig(App.GetService<ContextUserInfo>().Roles.ToDictionary(x => x.Id, x => x.DashboardLayout));
}
return UpdateAsync(req, [nameof(req.AppConfig)], null, a => a.Id == UserToken.Id);
return UpdateAsync(req, [nameof(req.AppConfig)], null, a => a.Id == UserToken.Id, null, true);
}
private ISelect<Sys_UserProfile, Sys_DicContent, Sys_DicContent, Sys_DicContent, Sys_DicContent> QueryInternal(QueryReq<QueryUserProfileReq> req)

View File

@ -132,7 +132,7 @@ public sealed class UserService(
ignoreCols.Add(nameof(Sys_User.Password));
}
_ = await UpdateAsync(entity, null, ignoreCols.ToArray()).ConfigureAwait(false);
_ = await UpdateAsync(entity, null, ignoreCols).ConfigureAwait(false);
// 档案表
if (req.Profile != null) {
@ -453,7 +453,8 @@ public sealed class UserService(
throw new NetAdminInvalidOperationException(Ln.);
}
_ = await UpdateAsync(dbUser with { LastLoginTime = DateTime.Now }, [nameof(Sys_User.LastLoginTime)]).ConfigureAwait(false);
_ = await UpdateAsync(dbUser with { LastLoginTime = DateTime.Now }, [nameof(Sys_User.LastLoginTime)], ignoreVersion: true)
.ConfigureAwait(false);
var tokenPayload = new Dictionary<string, object> { { nameof(ContextUserToken), dbUser.Adapt<ContextUserToken>() } };

View File

@ -18,7 +18,7 @@ public sealed class RequestLogCache(IDistributedCache cache, IRequestLogService
public async Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
#else
public Task<long> CountAsync(QueryReq<QueryRequestLogReq> req)
#endif
#endif
{
#if !DEBUG
var ret = await GetOrCreateAsync( //

View File

@ -62,9 +62,9 @@ public static class ServiceCollectionExtensions
/// <summary>
/// 添加定时任务
/// </summary>
public static IServiceCollection AddSchedules(this IServiceCollection me)
public static IServiceCollection AddSchedules(this IServiceCollection me, bool force = false)
{
return App.WebHostEnvironment.IsProduction()
return App.WebHostEnvironment.IsProduction() || force
? me.AddSchedule( //
builder => builder //
.AddJob<ScheduledJob>(true, Triggers.PeriodSeconds(1).SetRunOnStart(true))