using Mapster; using QYZH.InteractiveMagazine.Common.Extensions; using QYZH.InteractiveMagazine.Infrastructure.OSS; using QYZH.InteractiveMagazine.IService; using QYZH.InteractiveMagazine.Models.Base; using QYZH.InteractiveMagazine.Models.Common; using QYZH.InteractiveMagazine.Models.Dto.Journal; using QYZH.InteractiveMagazine.Models.Entity; using QYZH.InteractiveMagazine.Models.Enum; using QYZH.InteractiveMagazine.Repository; using SqlSugar; using Yitter.IdGenerator; namespace QYZH.InteractiveMagazine.Service; public class JournalCatalogService : BaseRepository, IJournalCatalogService { private readonly BaseRepository _JournalPageRepository; private readonly BaseRepository _JournalPageTaskRepository; private readonly OssService _ossService; public JournalCatalogService(BaseRepository JournalPageRepository, BaseRepository JournalPageTaskRepository, OssService ossService) { //_JournalRepository = JournalRepository; _JournalPageRepository = JournalPageRepository; _ossService = ossService; } public async Task> DetailAsync(long JournalId) { return await base.Queryable().Where(w => w.ParentId == 0 && w.JournalId == JournalId).OrderBy(o => o.Sort).ToChildListAsync(it => it.ParentId, 0) ?? []; } public async Task ImportAsync(JournalImportDto input) { var Journals = await Queryable().Where(w => w.Id == input.JournalId).FirstAsync(); BusinessException.ThrowIf(Journals.IsNull(), "未找到书本"); var pageNum = await _JournalPageRepository.Queryable().Where(w => w.JournalId == input.JournalId).MaxAsync(m => m.PageNum); BusinessException.ThrowIf(pageNum != 0, "已经存在书页"); var JournalCatalog = new JournalCatalog() { Id = YitIdHelper.NextId(), JournalId = input.JournalId, Name = "目录", ParentId = 0, Level = 0, Sort = 1, Type = JournalCatalogTypeEnum.Page, }; int sort = 1; var result = input.JournalCatalogs.Where(w => w.Type == 1).Select(s => { var JournalPage = new JournalPage { Id = YitIdHelper.NextId(), JournalId = input.JournalId, JournalCatalogId = JournalCatalog.Id, Sort = sort, PageNum = sort < input.Index ? 0 : sort - (input.Index - 1) }; var key = $"Journal/{input.JournalId}/{JournalPage.Id}/page.{s.Url.ToExtension()}"; _ossService.CopyObject(s.Url.RemoveDomain(), key); JournalPage.Url = key; //dotMatrixPage.PreviewUrl = key; sort = sort + 1; return new { JournalPage = JournalPage }; }).ToList(); await UseTranAsync(async () => { var data = await base.InsertAsync(JournalCatalog); var JournalPages = result.Select(x => x.JournalPage).ToList(); await _JournalPageRepository.InsertRangeAsync(JournalPages); var key = $"Journal/{input.JournalId}/Journal.pdf"; _ossService.CopyObject(input.PdfUrl.RemoveDomain(), key); await Updateable().SetColumns(s => s.PdfUrl, key) .SetColumns(s => s.Status, JournalStatusEnum.Created) .SetColumns(s => s.TotalPage, JournalPages.Count).Where(w => w.Id == input.JournalId) .ExecuteCommandAsync(); }); return result.Count; } /// /// 获取书分类及页码 /// /// /// public async Task> GetJournalCatalogListAsync(long JournalId) { var Journal = await Queryable().Where(w => w.Id == JournalId).FirstAsync(); BusinessException.ThrowIf(Journal.IsNull(), "未找到书本"); var pageNumList = await _JournalPageRepository.Queryable() .InnerJoin((a, b) => a.JournalCatalogId == b.Id) .Where((a, b) => a.JournalId == JournalId) .OrderBy((a, b) => a.PageNum) .Select((a, b) => new JournalCatalogTreeListDto { JournalId = a.JournalId, Id = b.Id, Name = b.Name, PageNum = a.PageNum, ParentName = SqlFunc.Subqueryable().Where(c => c.Id == b.ParentId).Select(c => c.Name), ParentId = b.ParentId, PageId = a.Id, }) .ToListAsync(); return pageNumList; } /// /// 获取书分类及页码 /// /// /// public async Task> GetJournalCataloTreeAsync(long JournalId) { var Journal = await Queryable().Where(w => w.Id == JournalId).FirstAsync(); BusinessException.ThrowIf(Journal.IsNull(), "未找到书本"); // 先查询所有目录 var allCatalogs = await base.Queryable() .Where(w => w.JournalId == JournalId) .OrderBy(o => o.Sort) .ToListAsync(); if (!allCatalogs.Any()) return new List(); // 再查询有页面对应的目录及页面信息 var pageNumList = await _JournalPageRepository.Queryable() .InnerJoin((a, b) => a.JournalCatalogId == b.Id) .Where((a, b) => a.JournalId == JournalId) .OrderBy((a, b) => a.PageNum) .Select((a, b) => new JournalCatalogTreeListDto { JournalId = a.JournalId, Id = b.Id, Name = b.Name, PageNum = a.PageNum, ParentName = SqlFunc.Subqueryable().Where(c => c.Id == b.ParentId).Select(c => c.Name), ParentId = b.ParentId, PageId = a.Id, }) .ToListAsync(); var tree = BuildCatalogTree(allCatalogs, pageNumList); return tree; } /// /// 构建目录树形结构(支持任意层级) /// /// 所有目录 /// 有页面对应的目录信息 /// private List BuildCatalogTree(List allCatalogs, List pageNumList) { // 构建有页面对应的目录到页面列表的映射 var catalogPagesMap = pageNumList .GroupBy(x => x.Id) .ToDictionary( g => g.Key, g => g.Select(p => new IcrJournalCatalogTreeDto { Id = p.PageId, Name = $"第{p.PageNum}页", Level = 0, Child = new List() }).ToList() ); // 构建所有目录节点字典 var catalogDict = new Dictionary(); var rootCatalogIds = new HashSet(); foreach (var catalog in allCatalogs) { var catalogId = catalog.Id; var parentId = catalog.ParentId; if (catalogDict.ContainsKey(catalogId)) continue; var node = new IcrJournalCatalogTreeDto { Id = catalogId, Name = catalog.Name, Level = 0, Child = catalogPagesMap.GetValueOrDefault(catalogId, new List()) }; catalogDict[catalogId] = node; if (parentId == 0) { rootCatalogIds.Add(catalogId); } } // 构建树形结构:将子节点挂载到父节点 foreach (var catalog in allCatalogs) { var catalogId = catalog.Id; var parentId = catalog.ParentId; if (parentId > 0 && catalogDict.ContainsKey(parentId) && catalogDict.ContainsKey(catalogId)) { var parentNode = catalogDict[parentId]; var childNode = catalogDict[catalogId]; if (!parentNode.Child.Any(c => c.Id == childNode.Id)) { parentNode.Child.Add(childNode); } } } // 计算每个节点的层级并返回根节点列表 var result = rootCatalogIds .Where(id => catalogDict.ContainsKey(id)) .Select(id => { var node = catalogDict[id]; node.Level = 1; CalculateChildLevels(node.Child, 2); return node; }) .ToList(); return result; } /// /// 递归计算子节点层级 /// private void CalculateChildLevels(List children, int level) { foreach (var child in children) { child.Level = level; if (child.Child != null && child.Child.Count > 0) { CalculateChildLevels(child.Child, level + 1); } } } /// /// 导入书籍目录 /// /// /// /// public async Task ImportCatalogAsync(long JournalId, List dtos) { dtos = dtos.Where(c => !string.IsNullOrWhiteSpace(c.Name)).ToList(); BusinessException.ThrowIf(dtos.Select(c => c.PageNum).Distinct().Count() != dtos.Count(), "存在重复的页码"); var Journal = await Queryable().Where(w => w.Id == JournalId).FirstAsync(); BusinessException.ThrowIf(Journal.IsNull(), "未找到书本"); var pageNums = dtos.Select(c => c.PageNum).ToList(); var pageNumNotExists = await _JournalPageRepository.Queryable().AnyAsync(w => w.JournalId == JournalId && !pageNums.Contains(w.PageNum)); BusinessException.ThrowIf(pageNumNotExists, "不存在的书页"); var firstCatelogGroup = dtos.Select(c => c.ParentName).Distinct().Select(c => new JournalCatalog { JournalId = JournalId, Id = YitIdHelper.NextId(), Level = 1, Name = c, }).ToList(); var subCatelogGroup = dtos.Select(c => new { c.Name, c.ParentName }).Distinct().Select(c => new { JournalId = JournalId, Id = YitIdHelper.NextId(), Level = 2, Name = c.Name, ParentId = firstCatelogGroup.FirstOrDefault(m => m.Name == c.ParentName)?.Id, ParentName = c.ParentName }).ToList(); var result = await UseTranAsync(async () => { await base.DeleteAsync(c => c.JournalId == JournalId); await base.InsertRangeAsync(firstCatelogGroup); await base.InsertRangeAsync(subCatelogGroup.Adapt>()); var JournalPages = await Queryable().Where(c => c.JournalId == JournalId).ToListAsync(); JournalPages.ForEach(c => { var cate = dtos.FirstOrDefault(m => m.PageNum == c.PageNum); c.JournalCatalogId = subCatelogGroup?.FirstOrDefault(m => m.Name == cate?.Name && m.ParentName == cate?.ParentName)?.Id ?? 0; }); await base.Context.Updateable(JournalPages).UpdateColumns(c => c.JournalCatalogId).ExecuteCommandAsync(); return true; }); return result; } public async Task InsertAsync(JournalCatalogInput input) { var map = input.Adapt(); map.Id = YitIdHelper.NextId(); await UseTranAsync(async () => { // 获取同级所有节点 var siblings = await base.Queryable().Where(w => w.JournalId == input.JournalId).Where(w => w.ParentId == input.ParentId).OrderBy(o => o.Sort).ToListAsync(); if (input.Position.HasValue && input.Position > 0 && input.Position <= siblings.Count) { // 调换位置,前面不变,后续加1即可 int validPosition = Math.Min(input.Position.Value, siblings.Count); map.Sort = validPosition; // 调整后续节点的SortOrder (+1) await base.Updateable() .SetColumns(x => x.Sort == x.Sort + 1) .Where(x => x.ParentId == input.ParentId && x.Sort >= validPosition) .ExecuteCommandAsync(); } else { // 默认追加到末尾 map.Sort = siblings.Count > 0 ? siblings.Max(x => x.Sort) + 1 : 0; } var data = await base.InsertAsync(map); if (input.Type == 1) { var Journal = await Queryable().Where(w => w.Id == input.JournalId).FirstAsync(); BusinessException.ThrowIf(Journal.IsNull(), "未找到书本"); //var dotMatrixPage = new DotMatrixPage() //{ // Id = YitIdHelper.NextId(), // DotMatrixNoteJournalId = input.JournalId, // WidthMilliMeter = Journal.Width, // HightMilliMeter = Journal.Height, //}; var JournalPage = new JournalPage { JournalId = input.JournalId, JournalCatalogId = map.Id, //DotMatrixPageId = dotMatrixPage.Id, PageNum = map.Sort, }; } }); return map.Id; } public async Task UpdateAsync(JournalCatalogUpdateInput input) { var result = await base.Updateable(input.Adapt()).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0; return result; } public async Task DeleteAsync(long id) { var catas = await base.Queryable().ToChildListAsync(it => it.ParentId, id); var ids = catas.Select(s => s.Id).ToList(); await UseTranAsync(async () => { // 删除目录 var cata = await base.Deleteable().Where(d => ids.Contains(d.Id)).ExecuteCommandAsync() > 0; BusinessException.ThrowIf(!cata, $"删除目录失败"); // 删除所有页/问题 var pages = await _JournalPageRepository.DeleteAsync(w => ids.Contains(w.JournalCatalogId)); BusinessException.ThrowIf(pages.IsNull(), $"删除数据失败"); //var x = await _JournalPageRepository.DeleteAsync(w => ids.Contains(w.JournalCatalogId)); //var y = await _JournalPageTaskRepository.DeleteAsync(w => ids.Contains(w.JournalCatalogId)); }); return true; } public async Task MoveAsync(MoveInput input) { // 1. 获取源节点并校验 var sourceNode = await base.Queryable().Where(x => x.Id == input.SourceId).FirstAsync(); BusinessException.ThrowIf(sourceNode.IsNull(), $"要移动的节点{input.SourceId}不存在"); // 2. 校验目标父节点(如果指定) if (input.TargetParentId > 0) { var targetParentExists = await base.Queryable().Where(x => x.Id == input.TargetParentId).AnyAsync(); BusinessException.ThrowIf(!targetParentExists, $"目标父节点{input.TargetParentId}不存在"); } await base.UseTranAsync(async () => { //var tree = await base.Queryable().Where(x => x.Id == input.SourceId).AsTreeCte().OrderBy(x => x.Level).ToTreeListAsync(); if (input.Position.HasValue) { await base.Updateable() .SetColumns(x => x.Sort == x.Sort + 1) .Where(x => x.ParentId == input.TargetParentId && x.Sort >= input.Position.Value) .ExecuteCommandAsync(); } // 3. 更新源节点的父节点 var updateCount = await base.Updateable() .SetColumns(x => x.ParentId, input.TargetParentId) .SetColumns(x => x.Sort, input.Position ?? 0) .Where(x => x.Id == input.SourceId) .ExecuteCommandAsync() > 0; BusinessException.ThrowIf(!updateCount, $"节点移动失败"); }); return true; } //public async Task CopyAsync(CopyInput input) //{ // // 1. 获取源节点并校验 // var sourceNode = await base.Queryable().Where(x => x.Id == input.SourceId).FirstAsync(); // BusinessException.ThrowIf(sourceNode.IsNull(), $"要移动的节点{input.SourceId}不存在"); // // 2. 校验目标父节点(如果指定) // if (input.TargetParentId > 0) // { // var targetParentExists = await base.Queryable().Where(x => x.Id == input.TargetParentId).AnyAsync(); // BusinessException.ThrowIf(!targetParentExists, $"目标父节点{input.TargetParentId}不存在"); // } // using var uow = _unitOfWorkManager.Begin(); // var tree = await base.Queryable().IncludeMany(c => c.Pages, then => then.IncludeMany(x => x.PageTasks)) // .Where(x => x.Id == input.SourceId) // .AsTreeCte() // .OrderBy(x => x.Level) // .ToTreeListAsync(); // if (input.Position.HasValue) // { // await base.Updateable() // .Set(x => x.Sort + 1) // .Where(x => x.ParentId == input.TargetParentId && x.Sort >= input.Position.Value) // .ExecuteCommandAsync(); // } // var copy = await DeepCopyAsync(tree.First(), input.TargetParentId); // BusinessException.ThrowIf(copy.IsNull(), $"复制节点失败"); // var data = await base.InsertAsync(copy); // BusinessException.ThrowIf(data.IsNull(), $"复制节点失败"); // var pages = copy.Where(s => s.Pages.NotNull()).Queryable()Many(s => s.Pages); // var tempPage = await _JournalPageRepository.InsertAsync(pages); // BusinessException.ThrowIf(tempPage.IsNull(), $"复制页面数据失败"); // //var task = pages.Where(w => w.PageTasks.NotNull()).Queryable()Many(s => s.PageTasks); // //var tempPageTask = await _JournalPageTaskRepository.InsertAsync(task); // //BusinessException.ThrowIf(tempPageTask.IsNull(), $"复制页面问题数据失败"); // uow.Commit(); // return true; //} //private async Task> DeepCopyAsync(IcrJournalCatalog source, long targetParentId, List? list = null) //{ // list ??= new List(); // var copy = new IcrJournalCatalog // { // Id = YitIdHelper.NextId(),// 设置新ID // Name = !list.Any() ? $"{source.Name} 副本" : source.Name, // ParentId = targetParentId, // Level = source.Level, // Sort = source.Sort, // }; // copy.Pages?.ForEach(page => // { // page.Id = YitIdHelper.NextId(); // 设置新页面ID // page.JournalCatalogId = copy.Id; // 设置为新目录的ID // page.PageTasks?.ForEach(task => // { // task.Id = YitIdHelper.NextId(); // 设置新问题ID // task.JournalPageId = page.Id; // 设置为新页面的ID // }); // }); // list.Add(copy); // // 递归复制子节点 // if (source.Childs != null && source.Childs.Any()) // { // foreach (var child in source.Childs) // { // await DeepCopyAsync(child, copy.Id, list); // } // } // return list; //} }