using Easy.Admin.Application.Config.Dtos; using Furion.ViewEngine; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Hosting; namespace Easy.Admin.Application.Config; /// /// 自定义配置业务 /// public class CustomConfigService : BaseService, ITransient { private readonly ISqlSugarRepository _customConfigRepository; private readonly IEasyCachingProvider _easyCachingProvider; private readonly IViewEngine _viewEngine; private readonly IWebHostEnvironment _environment; public CustomConfigService(ISqlSugarRepository customConfigRepository, IEasyCachingProvider easyCachingProvider, IViewEngine viewEngine, IWebHostEnvironment environment) : base(customConfigRepository) { _customConfigRepository = customConfigRepository; _easyCachingProvider = easyCachingProvider; _viewEngine = viewEngine; _environment = environment; } /// /// 获取强类型配置 /// /// /// [NonAction] public async Task Get() { var type = typeof(T); bool isList = typeof(IEnumerable).IsAssignableFrom(type); string code = type.IsGenericType && isList ? type.GenericTypeArguments[0].Name : type.Name; var value = await _easyCachingProvider.GetAsync($"{CacheConst.ConfigCacheKey}{code}", async () => { var queryable = _customConfigRepository.AsQueryable() .InnerJoin((config, item) => config.Id == item.ConfigId) .Where((config, item) => config.Code == code) .Select((config, item) => item.Json); string json; if (isList) { List list = await queryable.ToListAsync(); if (!list.Any()) return default(T); json = $"[{string.Join(",", list)}]"; } else { json = await queryable.FirstAsync(); } return string.IsNullOrWhiteSpace(json) ? default(T) : JsonConvert.DeserializeObject(json); }, TimeSpan.FromDays(1)); return value.Value; } /// /// 获取自定义配置 /// /// 自定义配置唯一编码 /// [DisplayName("获取自定义配置")] [HttpGet("getConfig")] public async Task GetConfig([FromQuery][Required(ErrorMessage = "缺少参数")] string code) { var value = await _easyCachingProvider.GetAsync($"{CacheConst.ConfigCacheKey}{code}", async () => { var c = await _customConfigRepository.GetFirstAsync(x => x.Code == code); if (c == null) return null; var queryable = _customConfigRepository.AsQueryable() .InnerJoin((config, item) => config.Id == item.ConfigId) .Where((config, item) => config.Code == code) .Select((config, item) => item.Json); if (c.IsMultiple) { List list = await queryable.ToListAsync(); if (!list.Any()) return null; return JArray.Parse($"[{string.Join(",", list)}]"); } string s = await queryable.FirstAsync(); return string.IsNullOrWhiteSpace(s) ? null : JObject.Parse(s); }, TimeSpan.FromDays(1)); return value.Value; } /// /// 自定义配置分页查询 /// /// /// [DisplayName("自定义配置分页查询")] [HttpGet] public async Task> Page([FromQuery] CustomConfigQueryInput dto) { return await _customConfigRepository.AsQueryable() .WhereIF(!string.IsNullOrWhiteSpace(dto.Name), x => x.Name.Contains(dto.Name)) .WhereIF(!string.IsNullOrWhiteSpace(dto.Code), x => x.Code.Contains(dto.Code)) .OrderByDescending(x => x.Id) .Select(x => new CustomConfigPageOutput { Id = x.Id, Status = x.Status, Remark = x.Remark, Name = x.Name, Code = x.Code, IsMultiple = x.IsMultiple, AllowCreationEntity = x.AllowCreationEntity, IsGenerate = x.IsGenerate, CreatedTime = x.CreatedTime }).ToPagedListAsync(dto); } /// /// 添加自定义配置 /// /// /// [DisplayName("添加自定义配置")] [HttpPost("add")] public async Task AddConfig(AddCustomConfigInput dto) { if (await _customConfigRepository.IsAnyAsync(x => x.Code == dto.Code)) { throw Oops.Bah("编码已存在"); } var config = dto.Adapt(); await _customConfigRepository.InsertAsync(config); await _easyCachingProvider.RemoveByPrefixAsync($"{CacheConst.ConfigCacheKey}{config.Code}"); } /// /// 修改自定义配置 /// /// /// [DisplayName("修改自定义配置")] [HttpPut("edit")] public async Task UpdateConfig(UpdateCustomConfigInput dto) { var config = await _customConfigRepository.GetByIdAsync(dto.Id); if (config == null) { throw Oops.Bah("无效参数"); } if (dto.Code != config.Code && await _customConfigRepository.IsAnyAsync(x => x.Code == dto.Code && x.Id != dto.Id)) { throw Oops.Bah("编码已存在"); } dto.Adapt(config); await _customConfigRepository.UpdateAsync(config); await _easyCachingProvider.RemoveByPrefixAsync($"{CacheConst.ConfigCacheKey}{config.Code}"); } /// /// 获取配置表单设计和表单数据 /// /// /// /// [DisplayName("获取配置表单设计和表单数据")] [HttpGet("GetFormJson")] public async Task GetFormJson([FromQuery] long id, [FromQuery] long? itemId) { var output = new CustomConfigDetailOutput() { ItemId = itemId ?? 0 }; string json = await _customConfigRepository.AsQueryable() .Where(x => x.Id == id) .Select(x => x.Json).FirstAsync(); if (string.IsNullOrWhiteSpace(json)) { return output; }; output.FormJson = JObject.Parse(json); if (itemId == 0) return output; var queryable = _customConfigRepository.AsSugarClient().Queryable(); var data = itemId.HasValue ? await queryable.Where(x => x.Id == itemId).Select(x => new { x.Id, x.Json }).FirstAsync() : await queryable.Where(x => x.ConfigId == id).Select(x => new { x.Id, x.Json }).FirstAsync(); if (data != null) { output.DataJson = JObject.Parse(data.Json); output.ItemId = data.Id; } return output; } /// /// 修改配置表单设计 /// /// /// [DisplayName("修改配置表单设计")] [HttpPatch] public async Task SetJson(CustomConfigSetJsonInput dto) { string json = JsonConvert.SerializeObject(dto.Json); await _customConfigRepository.UpdateSetColumnsTrueAsync(x => new CustomConfig() { Json = json }, x => x.Id == dto.Id); await ClearCache(); } /// /// 生成自定配置类 /// /// /// [DisplayName("生成自定配置类")] [HttpPost] public async Task Generate(KeyDto dto) { if (!_environment.IsDevelopment()) { throw Oops.Bah("生成配置类仅限开发环境使用"); } var config = await _customConfigRepository.GetByIdAsync(dto.Id); if (config == null) throw Oops.Bah("无效参数"); if (string.IsNullOrWhiteSpace(config.Json)) throw Oops.Bah("请配置设计"); var controls = ResolveJson(config.Json); if (!controls.Any()) throw Oops.Bah("请配置设计"); await GenerateCode(config.Code, controls); await _customConfigRepository.UpdateAsync(x => new CustomConfig() { IsGenerate = true }, x => x.Id == config.Id); await _easyCachingProvider.RemoveByPrefixAsync($"{CacheConst.ConfigCacheKey}{config.Code}"); } /// /// 删除自定义配置类 /// /// /// [DisplayName("删除自定义配置类")] [HttpPatch("deleteClass")] public async Task DeleteClass(KeyDto dto) { if (!_environment.IsDevelopment()) { throw Oops.Bah("删除配置类仅限开发环境使用"); } string className = await _customConfigRepository.AsQueryable().Where(x => x.Id == dto.Id).Select(x => x.Code).FirstAsync(); if (className == null) throw Oops.Oh("无效参数"); string path = Path.Combine(_environment.ContentRootPath.Replace(_environment.ApplicationName, ""), "Easy.Admin.Core/Config", $"{className}.cs"); if (System.IO.File.Exists(path)) { System.IO.File.Delete(path); } await _customConfigRepository.UpdateAsync(x => new CustomConfig() { IsGenerate = false }, x => x.Id == dto.Id); await _easyCachingProvider.RemoveByPrefixAsync($"{CacheConst.ConfigCacheKey}{className}"); } internal override Task ClearCache() { return _easyCachingProvider.RemoveByPrefixAsync(CacheConst.ConfigCacheKey); } /// /// 解析表单设计 /// /// /// [NonAction] public List ResolveJson(string json) { string s = "{\"key\":\\d+,\"type\":\"(input|select|date|switch|number|textarea|radio|checkbox|time|time-range|date-range|rate|color|slider|cascader|rich-editor|file-upload|picture-upload)\".*?\"id\".*?}"; string value = string.Join(",", Regex.Matches(json, s, RegexOptions.IgnoreCase).Select(x => x.Value)); string temp = $"[{value}]"; return JsonConvert.DeserializeObject>(temp); } /// /// 生成类文件 /// /// 类名 /// [NonAction] public async Task GenerateCode(string className, List options) { if (!options.Any()) return; List fields = new List(); options.ForEach(x => { string type = "string"; switch (x.Type.ToLower()) { case "select": case "checkbox": case "cascader": if (x.Options.Multiple || "checkbox".Equals(x.Type, StringComparison.CurrentCultureIgnoreCase)) { type = "List"; } break; case "date": type = x.Options.Required ? "DateTime" : "DateTime?"; break; case "switch": type = x.Options.Required ? "bool" : "bool?"; break; case "number": type = x.Options.Precision > 0 ? "double" : "int"; type += x.Options.Required ? string.Empty : "?"; break; case "time": type = x.Options.Required ? "TimeOnly" : "TimeOnly?"; break; case "time-range": type = "List"; break; case "date-range": type = "List"; break; case "rate": type = x.Options.AllowHalf ? "double" : "int"; type += x.Options.Required ? string.Empty : "?"; break; case "file-upload": case "picture-upload": type = x.Options.Limit > 1 ? "List" : "string"; break; default: type = "string"; break; } string remark = x.Options.OptionItems?.Any() ?? false ? string.Join(";", x.Options.OptionItems.Select(s => $"{s.Value}:{s.Label}")) : string.Empty; string template = @$" /// /// {x.Options.Label}{remark} /// public {type} {x.Options.Name} {{ get; set; }}"; fields.Add(template); }); string template = @" using System; using System.Collections.Generic; namespace Easy.Admin.Core.Config; public class @Model.ClassName {@foreach(var item in Model.Items) { @item } } "; string content = await _viewEngine.RunCompileFromCachedAsync(template, new { ClassName = className, Items = fields }); string path = Path.Combine(_environment.ContentRootPath.Replace(_environment.ApplicationName, ""), "Easy.Admin.Core/Config"); if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } string fileName = Path.Combine(path, $"{className}.cs"); if (System.IO.File.Exists(fileName)) { System.IO.File.Delete(fileName); } await System.IO.File.WriteAllTextAsync(fileName, content); } }