Files
WCS/Plugins/Wcs/Plugin.Cowain.Wcs/Services/WcsParamService.cs
2026-03-02 09:13:29 +08:00

306 lines
9.5 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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(SqlDbContext dbContext, ILogger<WcsParamService> logger, IMemoryCache memoryCache) : base(dbContext)
{
_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
{
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");
}
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!);
}
// 从数据库获取
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("参数不能为空");
}
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();
}
}
}
}