Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/JournalCatalogService.cs

527 lines
20 KiB
C#
Raw Normal View History


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<JournalCatalog>, IJournalCatalogService
{
private readonly BaseRepository<JournalPage> _JournalPageRepository;
private readonly BaseRepository<JournalPageTask> _JournalPageTaskRepository;
private readonly OssService _ossService;
public JournalCatalogService(BaseRepository<JournalPage> JournalPageRepository, BaseRepository<JournalPageTask> JournalPageTaskRepository, OssService ossService)
{
//_JournalRepository = JournalRepository;
_JournalPageRepository = JournalPageRepository;
_ossService = ossService;
}
public async Task<List<JournalCatalog>> 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<long> ImportAsync(JournalImportDto input)
{
var Journals = await Queryable<Journal>().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<Journal>().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;
}
/// <summary>
/// 获取书分类及页码
/// </summary>
/// <param name="JournalId"></param>
/// <returns></returns>
public async Task<List<JournalCatalogTreeListDto>> GetJournalCatalogListAsync(long JournalId)
{
var Journal = await Queryable<Journal>().Where(w => w.Id == JournalId).FirstAsync();
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
var pageNumList = await _JournalPageRepository.Queryable()
.InnerJoin<JournalCatalog>((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<JournalCatalog>().Where(c => c.Id == b.ParentId).Select(c => c.Name),
ParentId = b.ParentId,
PageId = a.Id,
})
.ToListAsync();
return pageNumList;
}
/// <summary>
/// 获取书分类及页码
/// </summary>
/// <param name="JournalId"></param>
/// <returns></returns>
public async Task<List<IcrJournalCatalogTreeDto>> GetJournalCataloTreeAsync(long JournalId)
{
var Journal = await Queryable<Journal>().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<IcrJournalCatalogTreeDto>();
// 再查询有页面对应的目录及页面信息
var pageNumList = await _JournalPageRepository.Queryable()
.InnerJoin<JournalCatalog>((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<JournalCatalog>().Where(c => c.Id == b.ParentId).Select(c => c.Name),
ParentId = b.ParentId,
PageId = a.Id,
})
.ToListAsync();
var tree = BuildCatalogTree(allCatalogs, pageNumList);
return tree;
}
/// <summary>
/// 构建目录树形结构(支持任意层级)
/// </summary>
/// <param name="allCatalogs">所有目录</param>
/// <param name="pageNumList">有页面对应的目录信息</param>
/// <returns></returns>
private List<IcrJournalCatalogTreeDto> BuildCatalogTree(List<JournalCatalog> allCatalogs, List<JournalCatalogTreeListDto> 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<IcrJournalCatalogTreeDto>()
}).ToList()
);
// 构建所有目录节点字典
var catalogDict = new Dictionary<long, IcrJournalCatalogTreeDto>();
var rootCatalogIds = new HashSet<long>();
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<IcrJournalCatalogTreeDto>())
};
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;
}
/// <summary>
/// 递归计算子节点层级
/// </summary>
private void CalculateChildLevels(List<IcrJournalCatalogTreeDto> children, int level)
{
foreach (var child in children)
{
child.Level = level;
if (child.Child != null && child.Child.Count > 0)
{
CalculateChildLevels(child.Child, level + 1);
}
}
}
/// <summary>
/// 导入书籍目录
/// </summary>
/// <param name="JournalId"></param>
/// <param name="dtos"></param>
/// <returns></returns>
public async Task<bool> ImportCatalogAsync(long JournalId, List<JournalCatalogTreeListDto> 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<Journal>().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<List<JournalCatalog>>());
var JournalPages = await Queryable<JournalPage>().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<JournalPage>(JournalPages).UpdateColumns(c => c.JournalCatalogId).ExecuteCommandAsync();
return true;
});
return result;
}
public async Task<long> InsertAsync(JournalCatalogInput input)
{
var map = input.Adapt<JournalCatalog>();
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<Journal>().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<bool> UpdateAsync(JournalCatalogUpdateInput input)
{
var result = await base.Updateable(input.Adapt<JournalCatalog>()).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0;
return result;
}
public async Task<bool> 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<bool> 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<bool> 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<List<IcrJournalCatalog>> DeepCopyAsync(IcrJournalCatalog source, long targetParentId, List<IcrJournalCatalog>? list = null)
//{
// list ??= new List<IcrJournalCatalog>();
// 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;
//}
}