307 lines
9.7 KiB
C#
307 lines
9.7 KiB
C#
using Cowain.Base.DBContext;
|
||
using Cowain.Base.Models;
|
||
using Cowain.Base.Services;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.Extensions.Caching.Memory;
|
||
using Microsoft.Extensions.Logging;
|
||
using Plugin.Cowain.Wcs.IServices;
|
||
using Plugin.Cowain.Wcs.Models.Dto;
|
||
using Plugin.Cowain.Wcs.ViewModels;
|
||
using System.Collections.Concurrent;
|
||
|
||
namespace Plugin.Cowain.Wcs.Services;
|
||
|
||
public class WcsParamService : BaseService, IWcsParamService
|
||
{
|
||
private readonly IMemoryCache _memoryCache;
|
||
private readonly ILogger<WcsParamService> _logger;
|
||
|
||
// 参数级别的锁,避免不同参数之间的锁竞争
|
||
private static readonly ConcurrentDictionary<string, SemaphoreSlim> _paramLocks = new();
|
||
|
||
// 缓存键前缀
|
||
private const string CACHE_KEY_PREFIX = "WcsParam_";
|
||
|
||
// 缓存过期时间(5分钟)
|
||
private static readonly TimeSpan CACHE_EXPIRATION = TimeSpan.FromMinutes(5);
|
||
|
||
public WcsParamService(IDbContextFactory<SqlDbContext> dbContextFactory, ILogger<WcsParamService> logger, IMemoryCache memoryCache) : base(dbContextFactory)
|
||
{
|
||
_memoryCache = memoryCache;
|
||
_logger = logger;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取参数级别的锁
|
||
/// </summary>
|
||
private SemaphoreSlim GetParamLock(string paramName)
|
||
{
|
||
return _paramLocks.GetOrAdd(paramName, _ => new SemaphoreSlim(1, 1));
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取缓存键
|
||
/// </summary>
|
||
private string GetCacheKey(string paramName)
|
||
{
|
||
return $"{CACHE_KEY_PREFIX}{paramName}";
|
||
}
|
||
|
||
/// <summary>
|
||
/// 清除指定参数的缓存
|
||
/// </summary>
|
||
private void ClearParamCache(string paramName)
|
||
{
|
||
string cacheKey = GetCacheKey(paramName);
|
||
_memoryCache.Remove(cacheKey);
|
||
_logger.LogInformation($"已清除参数 {paramName} 的缓存");
|
||
}
|
||
|
||
public async Task<ResultModel> AddAsync(WcsParamViewModel? viewModel)
|
||
{
|
||
if (viewModel == null)
|
||
{
|
||
return ResultModel.Error("viewModel不能为空");
|
||
}
|
||
if (string.IsNullOrWhiteSpace(viewModel.Name))
|
||
{
|
||
return ResultModel.Error("viewModel.Name不能为空");
|
||
}
|
||
if (string.IsNullOrWhiteSpace(viewModel.Param))
|
||
{
|
||
return ResultModel.Error("viewModel.Param不能为空");
|
||
}
|
||
|
||
var paramLock = GetParamLock(viewModel.Name);
|
||
await paramLock.WaitAsync();
|
||
|
||
try
|
||
{
|
||
using var dbContext = _dbContextFactory.CreateDbContext();
|
||
var dbSet = dbContext.GetDbSet<WcsParamDto>();
|
||
// 检查Name是否重复
|
||
var nameExists = await dbSet.AnyAsync(x => x.Name == viewModel.Name);
|
||
if (nameExists)
|
||
{
|
||
return ResultModel.Error("名称已存在");
|
||
}
|
||
|
||
var entity = new WcsParamDto
|
||
{
|
||
Name = viewModel.Name,
|
||
Param = viewModel.Param
|
||
};
|
||
await dbSet.AddAsync(entity);
|
||
var count = await dbContext.SaveChangesAsync();
|
||
|
||
if (count > 0)
|
||
{
|
||
// 清除相关缓存
|
||
ClearParamCache(viewModel.Name);
|
||
return ResultModel.Success("参数添加成功");
|
||
}
|
||
else
|
||
{
|
||
return ResultModel.Error("参数添加失败");
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
paramLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task<ResultModel> DeleteAsync(int id)
|
||
{
|
||
if (id <= 0)
|
||
{
|
||
return ResultModel.Error("id不能小于0");
|
||
}
|
||
using var dbContext = _dbContextFactory.CreateDbContext();
|
||
var DbSet = dbContext.GetDbSet<WcsParamDto>();
|
||
var existingModel = await DbSet.FirstOrDefaultAsync(x => x.Id == id);
|
||
if (existingModel == null)
|
||
{
|
||
return ResultModel.Error("id不存在");
|
||
}
|
||
|
||
string paramName = existingModel.Name;
|
||
var paramLock = GetParamLock(paramName);
|
||
await paramLock.WaitAsync();
|
||
|
||
try
|
||
{
|
||
DbSet.Remove(existingModel);
|
||
int count = await dbContext.SaveChangesAsync();
|
||
|
||
if (count > 0)
|
||
{
|
||
// 清除相关缓存
|
||
ClearParamCache(paramName);
|
||
return ResultModel.Success("参数删除成功");
|
||
}
|
||
else
|
||
{
|
||
return ResultModel.Error("参数删除失败");
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
paramLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task<ResultModel<WcsParamViewModel>> GetParamAsync(string? name)
|
||
{
|
||
if (string.IsNullOrWhiteSpace(name))
|
||
{
|
||
return ResultModel<WcsParamViewModel>.Error("参数名称不能为空");
|
||
}
|
||
|
||
string cacheKey = GetCacheKey(name);
|
||
|
||
// 第一次尝试从缓存获取
|
||
if (_memoryCache.TryGetValue(cacheKey, out WcsParamViewModel? cachedValue))
|
||
{
|
||
_logger.LogInformation($"从缓存获取参数 {name}: {cachedValue?.Param}");
|
||
return ResultModel<WcsParamViewModel>.Success(cachedValue!);
|
||
}
|
||
|
||
// 缓存不存在,使用锁保护数据库查询和缓存更新
|
||
var paramLock = GetParamLock(name);
|
||
await paramLock.WaitAsync();
|
||
|
||
try
|
||
{
|
||
// 双重检查,防止在等待锁期间其他线程已经更新了缓存
|
||
if (_memoryCache.TryGetValue(cacheKey, out cachedValue))
|
||
{
|
||
_logger.LogInformation($"双重检查:从缓存获取参数 {name}: {cachedValue?.Param}");
|
||
return ResultModel<WcsParamViewModel>.Success(cachedValue!);
|
||
}
|
||
// 从数据库获取
|
||
using var dbContext = _dbContextFactory.CreateDbContext();
|
||
var DbSet = dbContext.GetDbSet<WcsParamDto>();
|
||
var existingModel = await DbSet.FirstOrDefaultAsync(x => x.Name == name);
|
||
if (existingModel == null)
|
||
{
|
||
return ResultModel<WcsParamViewModel>.Error("名称不存在");
|
||
}
|
||
|
||
var result = new WcsParamViewModel
|
||
{
|
||
Id = existingModel.Id,
|
||
Name = existingModel.Name,
|
||
Param = existingModel.Param
|
||
};
|
||
|
||
// 将结果存入缓存
|
||
var cacheOptions = new MemoryCacheEntryOptions
|
||
{
|
||
AbsoluteExpirationRelativeToNow = CACHE_EXPIRATION,
|
||
SlidingExpiration = CACHE_EXPIRATION
|
||
};
|
||
_memoryCache.Set(cacheKey, result, cacheOptions);
|
||
|
||
_logger.LogInformation($"从数据库获取参数 {name}: {result.Param},已缓存");
|
||
return ResultModel<WcsParamViewModel>.Success(result);
|
||
}
|
||
finally
|
||
{
|
||
paramLock.Release();
|
||
}
|
||
}
|
||
|
||
public async Task<ResultModel> UpdateAsync(WcsParamViewModel? viewModel)
|
||
{
|
||
if (viewModel == null)
|
||
{
|
||
return ResultModel.Error("viewModel不能为空");
|
||
}
|
||
if (viewModel.Id <= 0)
|
||
{
|
||
return ResultModel.Error("ID不能小于0");
|
||
}
|
||
if (string.IsNullOrWhiteSpace(viewModel.Name))
|
||
{
|
||
return ResultModel.Error("名称不能为空");
|
||
}
|
||
if (string.IsNullOrWhiteSpace(viewModel.Param))
|
||
{
|
||
return ResultModel.Error("参数不能为空");
|
||
}
|
||
using var dbContext = _dbContextFactory.CreateDbContext();
|
||
var DbSet = dbContext.GetDbSet<WcsParamDto>();
|
||
var existingModel = await DbSet.FirstOrDefaultAsync(x => x.Id == viewModel.Id);
|
||
if (existingModel == null)
|
||
{
|
||
return ResultModel.Error("id不存在");
|
||
}
|
||
|
||
// 检查是否有同名(排除自己)
|
||
var nameExists = await DbSet.AnyAsync(x => x.Name == viewModel.Name && x.Id != viewModel.Id);
|
||
if (nameExists)
|
||
{
|
||
return ResultModel.Error("名称已存在");
|
||
}
|
||
|
||
// 记录旧名称,用于清除缓存
|
||
string oldName = existingModel.Name;
|
||
string newName = viewModel.Name;
|
||
|
||
// 获取两个参数的锁(如果名称不同)
|
||
var oldParamLock = GetParamLock(oldName);
|
||
var newParamLock = oldName == newName ? oldParamLock : GetParamLock(newName);
|
||
|
||
// 如果名称不同,需要同时获取两个锁,避免死锁
|
||
if (oldName != newName)
|
||
{
|
||
// 按字典序获取锁,避免死锁
|
||
var locks = new[] { oldParamLock, newParamLock }.OrderBy(l => l.GetHashCode()).ToArray();
|
||
await locks[0].WaitAsync();
|
||
await locks[1].WaitAsync();
|
||
}
|
||
else
|
||
{
|
||
await oldParamLock.WaitAsync();
|
||
}
|
||
|
||
try
|
||
{
|
||
// 更新字段
|
||
existingModel.Name = viewModel.Name;
|
||
existingModel.Param = viewModel.Param;
|
||
var count = await dbContext.SaveChangesAsync();
|
||
|
||
if (count > 0)
|
||
{
|
||
// 清除相关缓存(包括旧名称和新名称)
|
||
ClearParamCache(oldName);
|
||
if (oldName != newName)
|
||
{
|
||
ClearParamCache(newName);
|
||
}
|
||
return ResultModel.Success("参数更新成功");
|
||
}
|
||
else
|
||
{
|
||
return ResultModel.Error("参数更新失败");
|
||
}
|
||
}
|
||
finally
|
||
{
|
||
// 释放锁
|
||
if (oldName != newName)
|
||
{
|
||
var locks = new[] { oldParamLock, newParamLock }.OrderBy(l => l.GetHashCode()).ToArray();
|
||
locks[1].Release();
|
||
locks[0].Release();
|
||
}
|
||
else
|
||
{
|
||
oldParamLock.Release();
|
||
}
|
||
}
|
||
}
|
||
}
|