using System.Data.Common; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using StopShopping.EF; using StopShopping.Services.Models; using StopShopping.Services.Models.Req; using StopShopping.Services.Models.Resp; namespace StopShopping.Services.Implementions; public class CategoryService : ICategoryService { public CategoryService( IFileService fileService, StopShoppingContext dbContext, ILogger logger ) { _fileService = fileService; _dbContext = dbContext; _logger = logger; } private readonly IFileService _fileService; private readonly StopShoppingContext _dbContext; private readonly ILogger _logger; public async Task DeleteCategoryAsync(CategoryIdParams model) { var category = await _dbContext.Categories .AsNoTracking() .FirstOrDefaultAsync(c => c.Id == model.CategoryId && !c.Deleted); if (null == category) return ApiResponse.Failed("此分类已不存在,请刷新重试"); var anyProduct = await _dbContext.Products .AsNoTracking() .AnyAsync(p => _dbContext.Categories .AsNoTracking() .Where(c => c.Path.StartsWith(category.Path) && !c.Deleted) .Select(c => c.Id) .Contains(p.CategoryId)); if (anyProduct) return ApiResponse.Failed("分类下已有商品,不允许删除"); await _dbContext.Categories .Where(c => c.Path.StartsWith(category.Path) && !c.Deleted) .ExecuteDeleteAsync(); return ApiResponse.Succed(); } public async Task> EditCategoryAsync(EditCategoryParams model) { EF.Models.Category category; using var trans = await _dbContext.Database.BeginTransactionAsync(); try { short level = 1, order = 1; string path = string.Empty; var parent = await _dbContext.Categories .FirstOrDefaultAsync(c => c.Id == model.ParentId); if (null != parent) level = (short)(parent.Level + 1); if (model.Id > 0) { category = await _dbContext.Categories .FirstAsync(c => c.Id == model.Id && !c.Deleted); if (null == category) return new ApiResponse().Failed("此分类已不存在"); path = category.Path; order = category.Order; } else { category = new EF.Models.Category(); await _dbContext.Categories.AddAsync(category); order = await _dbContext.Categories .Where(c => c.ParentId == model.ParentId) .Select(c => c.Order) .DefaultIfEmpty() .MaxAsync(); } category.Level = level; if (!string.IsNullOrWhiteSpace(model.Logo)) category.Logo = model.Logo; category.Name = model.Name; category.Order = order; category.ParentId = model.ParentId; category.Path = path; await _dbContext.SaveChangesAsync(); if (!(model.Id > 0)) { category.Path = BuildPath(parent?.Path ?? "", category.Id); await _dbContext.SaveChangesAsync(); } await trans.CommitAsync(); var categoryResult = Cast(category); return new ApiResponse(categoryResult); } catch (DbException e) { await trans.RollbackAsync(); _logger.LogError(e, "新增/修改分类失败"); return new ApiResponse().Failed("数据库操作失败,请刷新重试"); } } public ApiResponse> GetCategoriesTree() { var qry = _dbContext.Categories .AsNoTracking() .Where(c => !c.Deleted) .OrderBy(c => c.ParentId) .ThenBy(c => c.Order) .AsEnumerable(); var result = new ApiResponse>(ToTree(qry)); return result; } public async Task ResortCategoryAsync(ResortCategoryParams model) { var curr = await _dbContext.Categories .FirstOrDefaultAsync(c => c.Id == model.Id && !c.Deleted); if (null == curr) return ApiResponse.Failed("此分类已不存在,请刷新重试"); var target = await _dbContext.Categories .FirstOrDefaultAsync(c => c.Order == model.TargetOrder && !c.Deleted); if (null == target) return ApiResponse.Failed("目标位置分类已不存在,请刷新重试"); target.Order = curr.Order; curr.Order = model.TargetOrder; await _dbContext.SaveChangesAsync(); return ApiResponse.Succed(); } #region private methods private List ToTree(IEnumerable models) { Dictionary idDicts = []; List categories = []; foreach (var model in models) { Category node = Cast(model); node.Children = []; idDicts.Add(node.Id, node); } foreach (var d in idDicts) { if (d.Value.ParentId > 0) idDicts[d.Value.ParentId].Children.Add(d.Value); else categories.Add(d.Value); } return categories; } private Category Cast(EF.Models.Category model) { return new() { Id = model.Id, LogoUrl = string.IsNullOrWhiteSpace(model.Logo) ? "" : _fileService.GetFileUrl(UploadScences.Category, model.Logo), Name = model.Name, Order = model.Order, ParentId = model.ParentId, Children = [] }; } private string BuildPath(string prefix, params int[] ids) { return string.Format("{0}/{1}/", prefix.TrimEnd('/') , string.Join('/', ids)); } #endregion }