using Mapster; using Microsoft.Extensions.Logging; using QYZH.InteractiveMagazine.Common.Extensions; using QYZH.InteractiveMagazine.Infrastructure.OSS; using QYZH.InteractiveMagazine.Infrastructure.RabbitMQ; using QYZH.InteractiveMagazine.IService; using QYZH.InteractiveMagazine.Models.Common; using QYZH.InteractiveMagazine.Models.Dto; using QYZH.InteractiveMagazine.Models.Dto.DotMatrix; 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 JournalService(BaseRepository JournalPageRepository, BaseRepository JournalPageTaskRepository, BaseRepository JournalPageTaskAnswerRepository, BaseRepository JournalCatalogRepository, BaseRepository dotFileRepository, BaseRepository dotFileDetailRepository, OssService ossService, ILogger logger) : BaseRepository, IJournalService { /// /// 查询List /// /// /// public async Task> GetListAsync(JournalQueryDto dto) { var query = await Queryable() .WhereIF(!string.IsNullOrWhiteSpace(dto.Name), a => a.Name.Contains(dto.Name)) .WhereIF(dto.Status.HasValue, a => a.Status == (int)dto.Status) .Select(a => new JournalDto(), true) .ToListAsync(); return query; } /// /// 分页查询 /// /// /// public async Task> GetPageListAsync(PageQueryModel search) { RefAsync totalNumber = 0; var dataList = await Queryable() //.InnerJoin((a, b) => a.Id == b.JournalId) .WhereIF(!string.IsNullOrWhiteSpace(search.Params.Name), a => a.Name.Contains(search.Params.Name)) .WhereIF(search.Params.Status.HasValue, a => a.Status == (int)search.Params.Status) .WhereIF(!string.IsNullOrWhiteSpace(search.Params.Name), a => a.Name.Contains(search.Params.Name)) .OrderByDescending(a => a.CreatedAt) .Select(a => new JournalDto(), true) .ToPageListAsync(search.PageIndex, search.PageSize, totalNumber); return new PageListModel(dataList, search.PageIndex, search.PageSize, totalNumber); } /// /// 创建杂志 /// /// 杂志信息 /// 新杂志ID public async Task> AddAsync(JournalAddDto input) { BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Name), "书籍名称不能为空", ResultCode.BAD_REQUEST); var journal = new Journal { Name = input.Name.Trim(), Title = input.Title, TotalPage = input.TotalPage, Status = (int)JournalStatusEnum.Created, PdfUrl = input.PdfUrl?.RemoveDomain(), Width = input.Width, Height = input.Height, Cover = input.Cover?.RemoveDomain(), BackCover = input.BackCover?.RemoveDomain(), PdfPreviewUrl = input.PdfPreviewUrl?.RemoveDomain(), CreatedBy = "System", UpdatedBy = "System", CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now, IsDeleted = false }; // Id 已由 YitIdHelper 预生成,先搬运文件到正式目录,再一次性插入 if (!string.IsNullOrWhiteSpace(input.Cover)) { var coverKey = $"journal/{journal.Id}/cover.{input.Cover.ToExtension()}"; ossService.CopyObject(input.Cover.RemoveDomain(), coverKey); journal.Cover = coverKey; } if (!string.IsNullOrWhiteSpace(input.BackCover)) { var backCoverKey = $"journal/{journal.Id}/backCover.{input.BackCover.ToExtension()}"; ossService.CopyObject(input.BackCover.RemoveDomain(), backCoverKey); journal.BackCover = backCoverKey; } if (!string.IsNullOrWhiteSpace(input.PdfUrl)) { var pdfKey = $"journal/{journal.Id}/Journal.{input.PdfUrl.ToExtension()}"; ossService.CopyObject(input.PdfUrl.RemoveDomain(), pdfKey); journal.PdfUrl = pdfKey; } var res = await base.InsertAsync(journal); if (!res) { new BusinessException("创建失败", ResultCode.GLOBAL_ERROR); } logger.LogInformation("杂志创建成功,ID: {Id}, 名称: {Name}", journal.Id, input.Name); return BaseResponse.Success(journal.Id); } /// /// 编辑 /// /// /// public async Task> EditAsync(JournalEditDto input) { var Journal = await base.GetByIdAsync(input.Id); BusinessException.ThrowIf(Journal.IsNull(), "不存在的Id", ResultCode.NOT_FOUND); BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Archive, "已归档不可编辑", ResultCode.CONFLICT); if (string.IsNullOrWhiteSpace(input.Cover)) { Journal.Cover = null; } else if (input.Cover.RemoveDomain() != Journal.Cover) { var key = $"journal/{Journal.Id}/cover.{input.Cover.ToExtension()}"; ossService.CopyObject(input.Cover.RemoveDomain(), key); Journal.Cover = key; } if (string.IsNullOrWhiteSpace(input.BackCover)) { Journal.BackCover = null; } else if (input.BackCover.RemoveDomain() != Journal.BackCover) { var backCoverkey = $"journal/{Journal.Id}/backCover.{input.BackCover.ToExtension()}"; ossService.CopyObject(input.BackCover.RemoveDomain(), backCoverkey); Journal.BackCover = backCoverkey; } if (string.IsNullOrWhiteSpace(input.PdfUrl)) { Journal.PdfUrl = null; } else if (input.PdfUrl.RemoveDomain() != Journal.PdfUrl) { var pdfUrlkey = $"journal/{Journal.Id}/Journal.{input.PdfUrl.ToExtension()}"; ossService.CopyObject(input.PdfUrl.RemoveDomain(), pdfUrlkey); Journal.PdfUrl = pdfUrlkey; } Journal.Width = input.Width; Journal.Height = input.Height; Journal.Name = input.Name; Journal.Title = input.Title; var res = await UseTranAsync(async () => { var result = await Context.Updateable(Journal).IgnoreColumns(c => c.Status).ExecuteCommandAsync(); return result > 0; }); return BaseResponse.Success(res); } public async Task> Tasks(long id) { var data = await JournalPageTaskRepository.Queryable().Where(w => w.JournalId == id) .Select(x => new JournalTaskOutput() { TaskId = x.Id, TaskNo = x.No, TaskSubType = x.Type }).ToListAsync(); return data.OrderBy(x => { var parts = x.TaskNo.Split('-').Select(int.Parse).ToArray(); return (parts[0], parts[1], parts[2], parts[3]); // 元组 }).ToList(); } public async Task DetailAsync(long id) { var Journal = await base.Queryable().Where(w => w.Id == id).Select().FirstAsync(); return Journal; } public async Task DeleteAsync(List ids) { BusinessException.ThrowIf(!base.Queryable().Any(w => ids.Contains(w.Id)), "ID不存在", ResultCode.NOT_FOUND); BusinessException.ThrowIf(base.Queryable().Any(w => ids.Contains(w.Id) && w.Status == (int)JournalStatusEnum.Archive), "已归档不可删除", ResultCode.CONFLICT); var result = await UseTranAsync(async () => { await base.DeleteAsync(d => ids.Contains(d.Id)); await JournalPageRepository.DeleteAsync(d => ids.Contains(d.JournalId)); await JournalCatalogRepository.DeleteAsync(d => ids.Contains(d.JournalId)); // 先查询要删除的 JournalPageTask Id 列表 var taskIds = await JournalPageTaskRepository.Queryable() .Where(d => ids.Contains(d.JournalId)) .Select(d => d.Id) .ToListAsync(); // 删除 JournalPageTaskAnswer if (taskIds.Any()) { await JournalPageTaskAnswerRepository.DeleteAsync(d => taskIds.Contains(d.JournalPageTaskId)); } // 再删除 JournalPageTask await JournalPageTaskRepository.DeleteAsync(d => ids.Contains(d.JournalId)); return true; }); //删除OSS上的journal/{JournalId}文件夹 if (result) { foreach (var JournalId in ids) { var prefix = $"journal/{JournalId}/"; var keys = ossService.ListObjects(prefix); if (keys?.Count > 0) { ossService.DeleteObjects(keys); } } } return result; } /// /// /// /// 书id /// 起始页码 /// public async Task StartPageAsync(long id, int index) { var oldIndex = await JournalPageRepository.Queryable().Where(w => w.JournalId == id && w.PageNum == 1).Select(x => x.Sort).FirstAsync(); // 如果位置没有变化,直接返回 if (index == oldIndex) return true; if (index > oldIndex) { // 向后移动的情况 var x = index - oldIndex; await JournalPageRepository.Updateable().SetColumns(s => s.PageNum, 0).Where(w => w.Sort < index).Where(w => w.JournalId == id).ExecuteCommandAsync(); await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.PageNum - x).Where(w => w.Sort >= index).Where(w => w.JournalId == id).ExecuteCommandAsync(); } else { // 向前移动的情况 //var x = oldIndex - input.Index; await JournalPageRepository.Updateable().SetColumns(s => s.PageNum, 0).Where(w => w.Sort < index).Where(w => w.JournalId == id).ExecuteCommandAsync(); // 将目标页面设置为第一页 await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.Sort - (index - 1)).Where(w => w.Sort >= index).Where(w => w.JournalId == id).ExecuteCommandAsync(); } // await JournalPageRepository.Updateable().SetColumns(s => s.PageNum == s.Sort).Where(w => w.JournalId == input.Id).ExecuteCommandAsync(); return true; } public async Task PrintCodeAsync(long id) { var Journal = await base.Queryable().Where(w => w.Id == id).FirstAsync(); BusinessException.ThrowIf(Journal.IsNull(), "不存在书", ResultCode.NOT_FOUND); BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Codeing, "正在生成中...", ResultCode.CONFLICT); var pages = await JournalPageRepository.Queryable().Where(w => w.JournalId == id).ToListAsync(); BusinessException.ThrowIf(pages.IsNull(), "不存在页", ResultCode.NOT_FOUND); var output = new DotMatrixOutput() { Method = "Journal", Name = Journal.Name, NoteId = id, PdfUrl = Journal.PdfUrl }; return output; } public async Task ResultReportAsync(DotMatrixNoteJournalReportInput input) { if (input.FileUrl.IsNull() && input.FileKey.IsNull()) { return await Updateable().SetColumns(s => s.Status, input.Success ? JournalStatusEnum.CodeSuccess : JournalStatusEnum.CodeFail) .SetColumns(s => s.PdfPreviewUrl, input.FileKey) .Where(w => w.Id == input.Id) .ExecuteCommandAsync() > 0; } return await Updateable().SetColumns(s => s.Status, JournalStatusEnum.CodeFail).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0; } public async Task StatusAsync(long id, JournalStatusEnum status) { var book = await base.GetByIdAsync(id); var tasks = await Context.Queryable().Where(w => w.JournalId == id).ToListAsync(); BusinessException.ThrowIf(tasks.Count == 0 && status == JournalStatusEnum.Archive, "未添加任何题目,无法归档", ResultCode.UNPROCESSABLE_ENTITY); BusinessException.ThrowIf(tasks.Any(a => string.IsNullOrWhiteSpace(a.TaskUrl)) && status == JournalStatusEnum.Archive, $"{string.Join(',', tasks.Where(a => string.IsNullOrWhiteSpace(a.TaskUrl)).Select(a => a.No).ToList())}未保存,无法归档", ResultCode.UNPROCESSABLE_ENTITY); var res = await base.Updateable().SetColumns(s => s.Status, status).Where(w => w.Id == id).ExecuteCommandAsync() > 0; return res; } }