using Microsoft.Extensions.Caching.Memory; using Model; using Model.Dto.System; using Model.Entity.System; using Model.Mapper; using ORM; using Service.Interface; using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace Service.Implement { /// /// 系统参数配置 服务实现 /// 缓存策略:按 ParamKey 缓存值;增删改时失效对应 Key(Key 变更时新旧都失效) /// public class SystemParamService : ISystemParamService { private readonly IMemoryCache _cache; private readonly ICurrentUser _currentUser; private static readonly object _cacheLock = new(); public SystemParamService(IMemoryCache cache, ICurrentUser currentUser) { _cache = cache; _currentUser = currentUser; } // ==================== 管理侧 ==================== public async Task>> GetListPagedAsync(int pageIndex, int pageSize, RefAsync total, string? keyword = null, string? group = null) { try { var list = await SqlSugarContext.DbContext.Queryable() .Where(x => x.IsDel == 0) .WhereIF(!string.IsNullOrWhiteSpace(keyword), x => x.ParamKey.Contains(keyword!) || x.ParamName.Contains(keyword!)) .WhereIF(!string.IsNullOrWhiteSpace(group), x => x.Group == group) .OrderBy(x => x.Sort).OrderBy(x => x.CreateTime, OrderByType.Desc) .ToPageListAsync(pageIndex, pageSize, total); return Result>.Success(list.ToDtoList()); } catch (Exception ex) { return Result>.Error("查询系统参数失败", ex); } } public async Task> GetByIdAsync(long id) { try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("系统参数不存在或已被删除"); return Result.Success(entity.ToDto()); } catch (Exception ex) { return Result.Error("查询系统参数详情失败", ex); } } public async Task> AddAsync(SystemParamDto dto) { if (dto == null || string.IsNullOrWhiteSpace(dto.ParamKey) || string.IsNullOrWhiteSpace(dto.ParamName)) return Result.Error("参数键和名称不能为空"); try { var keyExists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.ParamKey == dto.ParamKey && x.IsDel == 0).AnyAsync(); if (keyExists) return Result.Error($"参数键「{dto.ParamKey}」已存在"); var entity = dto.ToEntity(); entity.Id = 0; entity.IsSystem = 0; // 用户新增的不是内置参数 entity.CreateTime = DateTime.Now; entity.LastUpdateUser = _currentUser.UserName; entity.LastUpdateTime = DateTime.Now; var id = await SqlSugarContext.DbContext.Insertable(entity).ExecuteReturnSnowflakeIdAsync(); entity.Id = id; InvalidateCache(entity.ParamKey); return Result.Success(entity.ToDto()); } catch (Exception ex) { return Result.Error("新增系统参数失败", ex); } } public async Task> UpdateAsync(SystemParamDto dto) { if (dto == null || !long.TryParse(dto.Id, out var id) || id <= 0) return Result.Error("参数Id无效"); try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("系统参数不存在或已被删除"); // Key 变更时校验唯一 if (!string.Equals(entity.ParamKey, dto.ParamKey, StringComparison.OrdinalIgnoreCase)) { var keyExists = await SqlSugarContext.DbContext.Queryable() .Where(x => x.ParamKey == dto.ParamKey && x.Id != id && x.IsDel == 0).AnyAsync(); if (keyExists) return Result.Error($"参数键「{dto.ParamKey}」已存在"); } var oldKey = entity.ParamKey; var now = DateTime.Now; var userName = _currentUser.UserName; if (entity.IsSystem == 1) { // 内置参数仅允许改 Value/Remark,其它字段保留 entity.ParamValue = dto.ParamValue; entity.Remark = dto.Remark; entity.LastUpdateUser = userName; entity.LastUpdateTime = now; await SqlSugarContext.DbContext.Updateable(entity) .UpdateColumns(x => new { x.ParamValue, x.Remark, x.LastUpdateUser, x.LastUpdateTime }) .ExecuteCommandAsync(); } else { // 用户自定义参数:全字段更新(审计字段由服务端控管) var newEntity = dto.ToEntity(); newEntity.Id = id; newEntity.IsSystem = 0; newEntity.CreateTime = entity.CreateTime; newEntity.LastUpdateUser = userName; newEntity.LastUpdateTime = now; await SqlSugarContext.DbContext.Updateable(newEntity) .IgnoreColumns(x => new { x.CreateTime, x.IsDel }) .ExecuteCommandAsync(); entity = newEntity; } InvalidateCache(oldKey); InvalidateCache(entity.ParamKey); return Result.Success(entity.ToDto()); } catch (Exception ex) { return Result.Error("修改系统参数失败", ex); } } public async Task DeleteAsync(long id) { if (id <= 0) return Result.Error("参数Id无效"); try { var entity = await SqlSugarContext.DbContext.Queryable() .Where(x => x.Id == id && x.IsDel == 0).FirstAsync(); if (entity == null) return Result.Error("系统参数不存在或已被删除"); if (entity.IsSystem == 1) return Result.Error($"内置参数「{entity.ParamName}」禁止删除,仅允许修改值"); await SqlSugarContext.DbContext.Updateable() .SetColumns(x => x.IsDel == 1) .Where(x => x.Id == id).ExecuteCommandAsync(); InvalidateCache(entity.ParamKey); return Result.Success(); } catch (Exception ex) { return Result.Error("删除系统参数失败", ex); } } public Task ReloadCacheAsync() { // IMemoryCache 非强类型缓存清除,此处复用一个枚举 Key 的策略:清除所有 sysparam: 前缀缓存需要 ICache 抽象; // 当前实现:通过 Coherent MemoryCache 实例的 compact,按约定不实现细粒度清除,仅返回成功提示由进程内自然过期 // 注:增删改单条时已按 Key 失效;本接口预留给运维紧急场景,下次访问会重新加载 try { // 由于 IMemoryCache 没有枚举能力,这里仅作占位返回;如需全量清除,请重启服务或改用 IDistributedCache return Task.FromResult(Result.Success()); } catch (Exception ex) { return Task.FromResult(Result.Error("清空缓存失败", ex)); } } // ==================== 业务侧:按 Key 取值(带缓存) ==================== public async Task> GetValueAsync(string key) { if (string.IsNullOrWhiteSpace(key)) return Result.Error("参数键不能为空"); try { var value = await LoadValueByKeyAsync(key); return Result.Success(value ?? string.Empty); } catch (Exception ex) { return Result.Error("查询参数值失败", ex); } } public async Task>> GetValuesAsync(string[] keys) { if (keys == null || keys.Length == 0) return Result>.Error("参数键列表不能为空"); try { var distinctKeys = keys.Where(k => !string.IsNullOrWhiteSpace(k)).Distinct().ToList(); var list = new List(distinctKeys.Count); foreach (var k in distinctKeys) { var v = await LoadValueByKeyAsync(k); list.Add(new SystemParamValueDto { Key = k, Value = v ?? string.Empty }); } return Result>.Success(list); } catch (Exception ex) { return Result>.Error("批量查询参数值失败", ex); } } // ==================== 私有工具 ==================== private async Task LoadValueByKeyAsync(string key) { var cacheKey = $"sysparam:{key}"; if (!_cache.TryGetValue(cacheKey, out string? cached)) { lock (_cacheLock) { if (!_cache.TryGetValue(cacheKey, out cached)) { cached = SqlSugarContext.DbContext.Queryable() .Where(x => x.ParamKey == key && x.IsDel == 0) .Select(x => x.ParamValue).First(); _cache.Set(cacheKey, cached ?? string.Empty, TimeSpan.FromHours(1)); } } } return cached; } private void InvalidateCache(string? key) { if (string.IsNullOrWhiteSpace(key)) return; _cache.Remove($"sysparam:{key}"); } } }