2026-06-23 17:55:48 +08:00
|
|
|
|
using Mapster;
|
2026-06-11 17:01:24 +08:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Common.Extensions;
|
2026-07-01 10:56:18 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Common.Helpers;
|
2026-06-11 17:01:24 +08:00
|
|
|
|
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;
|
2026-07-10 10:44:00 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Models.Dto.RabbitMQ;
|
2026-06-11 17:01:24 +08:00
|
|
|
|
using QYZH.InteractiveMagazine.Models.Entity;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Models.Enum;
|
|
|
|
|
|
using QYZH.InteractiveMagazine.Repository;
|
|
|
|
|
|
using SqlSugar;
|
2026-07-10 10:44:00 +08:00
|
|
|
|
using System.Text.Json;
|
2026-06-11 17:01:24 +08:00
|
|
|
|
using Yitter.IdGenerator;
|
|
|
|
|
|
|
|
|
|
|
|
namespace QYZH.InteractiveMagazine.Service;
|
|
|
|
|
|
|
|
|
|
|
|
public class JournalService(BaseRepository<JournalPage> JournalPageRepository,
|
|
|
|
|
|
BaseRepository<JournalPageTask> JournalPageTaskRepository,
|
2026-06-23 17:55:48 +08:00
|
|
|
|
BaseRepository<JournalPageTaskAnswer> JournalPageTaskAnswerRepository,
|
2026-06-22 16:56:05 +08:00
|
|
|
|
BaseRepository<JournalCatalog> JournalCatalogRepository,
|
2026-06-11 17:01:24 +08:00
|
|
|
|
BaseRepository<DotFile> dotFileRepository,
|
2026-06-30 11:03:39 +08:00
|
|
|
|
BaseRepository<DotFileDetail> dotFileDetailRepository,
|
|
|
|
|
|
OssService ossService,
|
2026-07-10 10:44:00 +08:00
|
|
|
|
IMessagePublishService messagePublishService,
|
2026-06-30 11:03:39 +08:00
|
|
|
|
ILogger<JournalService> logger) : BaseRepository<Journal>, IJournalService
|
2026-06-11 17:01:24 +08:00
|
|
|
|
{
|
2026-06-30 11:03:39 +08:00
|
|
|
|
private const string JournalExchange = "ex.journal";
|
|
|
|
|
|
private const string PublishBookQueue = "mq.journal.publish.book";
|
|
|
|
|
|
private const string PublishBookRoutingKey = "rk.journal.publish.book";
|
|
|
|
|
|
private const string PublishBookPageQueue = "mq.journal.publish.bookpage";
|
|
|
|
|
|
private const string PublishBookPageRoutingKey = "rk.journal.publish.bookpage";
|
|
|
|
|
|
|
2026-06-11 17:01:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 查询List
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dto"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public async Task<List<JournalDto>> 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 分页查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="search"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public async Task<PageListModel<JournalDto>> GetPageListAsync(PageQueryModel<JournalQueryDto> search)
|
|
|
|
|
|
{
|
|
|
|
|
|
RefAsync<int> totalNumber = 0;
|
|
|
|
|
|
|
|
|
|
|
|
var dataList = await Queryable()
|
|
|
|
|
|
//.InnerJoin<IcrJournalOrganization>((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<JournalDto>(dataList, search.PageIndex, search.PageSize, totalNumber);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-16 16:54:02 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建杂志
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="input">杂志信息</param>
|
|
|
|
|
|
/// <returns>新杂志ID</returns>
|
|
|
|
|
|
public async Task<BaseResponse<long>> AddAsync(JournalAddDto input)
|
|
|
|
|
|
{
|
2026-06-29 16:34:26 +08:00
|
|
|
|
BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Name), "书籍名称不能为空", ResultCode.BAD_REQUEST);
|
2026-06-16 16:54:02 +08:00
|
|
|
|
|
|
|
|
|
|
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(),
|
2026-06-30 11:03:39 +08:00
|
|
|
|
StartTime = input.StartTime,
|
|
|
|
|
|
EndTime = input.EndTime,
|
2026-06-16 16:54:02 +08:00
|
|
|
|
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)
|
|
|
|
|
|
{
|
2026-06-29 16:34:26 +08:00
|
|
|
|
new BusinessException("创建失败", ResultCode.GLOBAL_ERROR);
|
2026-06-16 16:54:02 +08:00
|
|
|
|
}
|
|
|
|
|
|
logger.LogInformation("杂志创建成功,ID: {Id}, 名称: {Name}", journal.Id, input.Name);
|
|
|
|
|
|
return BaseResponse<long>.Success(journal.Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-11 17:01:24 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 编辑
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="input"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public async Task<BaseResponse<bool>> EditAsync(JournalEditDto input)
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
var Journal = await base.GetByIdAsync(input.Id);
|
2026-06-29 16:34:26 +08:00
|
|
|
|
BusinessException.ThrowIf(Journal.IsNull(), "不存在的Id", ResultCode.NOT_FOUND);
|
|
|
|
|
|
BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Archive, "已归档不可编辑", ResultCode.CONFLICT);
|
2026-06-11 17:01:24 +08:00
|
|
|
|
if (string.IsNullOrWhiteSpace(input.Cover))
|
|
|
|
|
|
{
|
|
|
|
|
|
Journal.Cover = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (input.Cover.RemoveDomain() != Journal.Cover)
|
|
|
|
|
|
{
|
2026-06-16 16:54:02 +08:00
|
|
|
|
var key = $"journal/{Journal.Id}/cover.{input.Cover.ToExtension()}";
|
2026-06-11 17:01:24 +08:00
|
|
|
|
ossService.CopyObject(input.Cover.RemoveDomain(), key);
|
|
|
|
|
|
Journal.Cover = key;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(input.BackCover))
|
|
|
|
|
|
{
|
|
|
|
|
|
Journal.BackCover = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (input.BackCover.RemoveDomain() != Journal.BackCover)
|
|
|
|
|
|
{
|
2026-06-16 16:54:02 +08:00
|
|
|
|
var backCoverkey = $"journal/{Journal.Id}/backCover.{input.BackCover.ToExtension()}";
|
2026-06-11 17:01:24 +08:00
|
|
|
|
ossService.CopyObject(input.BackCover.RemoveDomain(), backCoverkey);
|
|
|
|
|
|
Journal.BackCover = backCoverkey;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(input.PdfUrl))
|
|
|
|
|
|
{
|
|
|
|
|
|
Journal.PdfUrl = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (input.PdfUrl.RemoveDomain() != Journal.PdfUrl)
|
|
|
|
|
|
{
|
2026-06-16 16:54:02 +08:00
|
|
|
|
var pdfUrlkey = $"journal/{Journal.Id}/Journal.{input.PdfUrl.ToExtension()}";
|
2026-06-11 17:01:24 +08:00
|
|
|
|
ossService.CopyObject(input.PdfUrl.RemoveDomain(), pdfUrlkey);
|
|
|
|
|
|
Journal.PdfUrl = pdfUrlkey;
|
|
|
|
|
|
}
|
|
|
|
|
|
Journal.Width = input.Width;
|
|
|
|
|
|
Journal.Height = input.Height;
|
|
|
|
|
|
Journal.Name = input.Name;
|
2026-06-16 16:54:02 +08:00
|
|
|
|
Journal.Title = input.Title;
|
2026-06-30 11:03:39 +08:00
|
|
|
|
Journal.StartTime = input.StartTime;
|
|
|
|
|
|
Journal.EndTime = input.EndTime;
|
|
|
|
|
|
Journal.UpdatedAt = DateTime.Now;
|
2026-06-11 17:01:24 +08:00
|
|
|
|
|
|
|
|
|
|
var res = await UseTranAsync(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
var result = await Context.Updateable(Journal).IgnoreColumns(c => c.Status).ExecuteCommandAsync();
|
|
|
|
|
|
return result > 0;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return BaseResponse<bool>.Success(res);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<List<JournalTaskOutput>> 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<JournalDto> DetailAsync(long id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var Journal = await base.Queryable().Where(w => w.Id == id).Select<JournalDto>().FirstAsync();
|
|
|
|
|
|
return Journal;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<bool> DeleteAsync(List<long> ids)
|
|
|
|
|
|
{
|
2026-06-29 16:34:26 +08:00
|
|
|
|
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);
|
2026-06-11 17:01:24 +08:00
|
|
|
|
var result = await UseTranAsync(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
await base.DeleteAsync(d => ids.Contains(d.Id));
|
|
|
|
|
|
await JournalPageRepository.DeleteAsync(d => ids.Contains(d.JournalId));
|
2026-06-22 16:56:05 +08:00
|
|
|
|
await JournalCatalogRepository.DeleteAsync(d => ids.Contains(d.JournalId));
|
2026-07-01 10:56:18 +08:00
|
|
|
|
|
2026-06-23 17:55:48 +08:00
|
|
|
|
// 先查询要删除的 JournalPageTask Id 列表
|
|
|
|
|
|
var taskIds = await JournalPageTaskRepository.Queryable()
|
|
|
|
|
|
.Where(d => ids.Contains(d.JournalId))
|
|
|
|
|
|
.Select(d => d.Id)
|
|
|
|
|
|
.ToListAsync();
|
2026-07-01 10:56:18 +08:00
|
|
|
|
|
2026-06-23 17:55:48 +08:00
|
|
|
|
// 删除 JournalPageTaskAnswer
|
|
|
|
|
|
if (taskIds.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
await JournalPageTaskAnswerRepository.DeleteAsync(d => taskIds.Contains(d.JournalPageTaskId));
|
|
|
|
|
|
}
|
2026-07-01 10:56:18 +08:00
|
|
|
|
|
2026-06-23 17:55:48 +08:00
|
|
|
|
// 再删除 JournalPageTask
|
2026-06-11 17:01:24 +08:00
|
|
|
|
await JournalPageTaskRepository.DeleteAsync(d => ids.Contains(d.JournalId));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-16 16:54:02 +08:00
|
|
|
|
//删除OSS上的journal/{JournalId}文件夹
|
2026-06-11 17:01:24 +08:00
|
|
|
|
if (result)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var JournalId in ids)
|
|
|
|
|
|
{
|
2026-06-16 16:54:02 +08:00
|
|
|
|
var prefix = $"journal/{JournalId}/";
|
2026-06-11 17:01:24 +08:00
|
|
|
|
var keys = ossService.ListObjects(prefix);
|
|
|
|
|
|
if (keys?.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ossService.DeleteObjects(keys);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
///
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="id">书id</param>
|
|
|
|
|
|
/// <param name="index">起始页码</param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public async Task<bool> 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<DotMatrixOutput> PrintCodeAsync(long id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var Journal = await base.Queryable().Where(w => w.Id == id).FirstAsync();
|
2026-06-29 16:34:26 +08:00
|
|
|
|
BusinessException.ThrowIf(Journal.IsNull(), "不存在书", ResultCode.NOT_FOUND);
|
|
|
|
|
|
BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Codeing, "正在生成中...", ResultCode.CONFLICT);
|
2026-06-11 17:01:24 +08:00
|
|
|
|
|
|
|
|
|
|
var pages = await JournalPageRepository.Queryable().Where(w => w.JournalId == id).ToListAsync();
|
2026-06-29 16:34:26 +08:00
|
|
|
|
BusinessException.ThrowIf(pages.IsNull(), "不存在页", ResultCode.NOT_FOUND);
|
2026-06-11 17:01:24 +08:00
|
|
|
|
|
|
|
|
|
|
var output = new DotMatrixOutput()
|
|
|
|
|
|
{
|
|
|
|
|
|
Method = "Journal",
|
|
|
|
|
|
Name = Journal.Name,
|
|
|
|
|
|
NoteId = id,
|
|
|
|
|
|
PdfUrl = Journal.PdfUrl
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
return output;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async Task<bool> 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<bool> StatusAsync(long id, JournalStatusEnum status)
|
|
|
|
|
|
{
|
|
|
|
|
|
var book = await base.GetByIdAsync(id);
|
|
|
|
|
|
var tasks = await Context.Queryable<JournalPageTask>().Where(w => w.JournalId == id).ToListAsync();
|
2026-06-29 16:34:26 +08:00
|
|
|
|
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);
|
2026-06-11 17:01:24 +08:00
|
|
|
|
var res = await base.Updateable().SetColumns(s => s.Status, status).Where(w => w.Id == id).ExecuteCommandAsync() > 0;
|
|
|
|
|
|
return res;
|
|
|
|
|
|
}
|
2026-06-30 11:03:39 +08:00
|
|
|
|
|
|
|
|
|
|
public async Task<bool> PublishAsync(long id)
|
|
|
|
|
|
{
|
|
|
|
|
|
var book = await base.GetByIdAsync(id);
|
|
|
|
|
|
BusinessException.ThrowIf(book == null || book.IsDeleted, "杂志不存在", ResultCode.NOT_FOUND);
|
|
|
|
|
|
|
|
|
|
|
|
var pages = await JournalPageRepository.Queryable()
|
|
|
|
|
|
.Where(x => x.JournalId == id && !x.IsDeleted)
|
|
|
|
|
|
.OrderBy(x => x.PageNum)
|
|
|
|
|
|
.OrderBy(x => x.Sort)
|
2026-07-01 10:56:18 +08:00
|
|
|
|
.Select(x => new
|
|
|
|
|
|
{
|
|
|
|
|
|
x.Id,
|
|
|
|
|
|
x.PageNo,
|
|
|
|
|
|
x.Layout,
|
|
|
|
|
|
x.Url
|
|
|
|
|
|
})
|
2026-06-30 11:03:39 +08:00
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
BusinessException.ThrowIf(pages.Count == 0, "书籍未添加任何书页,无法发布", ResultCode.UNPROCESSABLE_ENTITY);
|
|
|
|
|
|
|
|
|
|
|
|
BusinessException.ThrowIf(!book.StartTime.HasValue, "发布开始时间不能为空", ResultCode.UNPROCESSABLE_ENTITY);
|
|
|
|
|
|
BusinessException.ThrowIf(!book.EndTime.HasValue, "发布结束时间不能为空", ResultCode.UNPROCESSABLE_ENTITY);
|
|
|
|
|
|
BusinessException.ThrowIf(book.EndTime < book.StartTime, "发布结束时间不能早于开始时间", ResultCode.UNPROCESSABLE_ENTITY);
|
|
|
|
|
|
|
2026-07-01 10:56:18 +08:00
|
|
|
|
var pageIds = pages.Select(x => x.Id).ToList();
|
|
|
|
|
|
var pageQuestions = await JournalPageTaskRepository.Queryable()
|
|
|
|
|
|
.Where(x => x.JournalId == id && pageIds.Contains(x.JournalPageId) && !x.IsDeleted)
|
|
|
|
|
|
.Select(x => new
|
|
|
|
|
|
{
|
|
|
|
|
|
x.JournalPageId,
|
|
|
|
|
|
x.Id,
|
|
|
|
|
|
x.No
|
|
|
|
|
|
})
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
var questionIdsByPageId = pageQuestions
|
|
|
|
|
|
.GroupBy(x => x.JournalPageId)
|
|
|
|
|
|
.ToDictionary(
|
|
|
|
|
|
x => x.Key,
|
|
|
|
|
|
x => string.Join(',', x.OrderBy(q => ParseTaskNo(q.No)).Select(q => q.Id)));
|
|
|
|
|
|
|
2026-07-10 10:44:00 +08:00
|
|
|
|
var publishMessages = new List<MessagePublishInput<object>>
|
2026-06-30 11:03:39 +08:00
|
|
|
|
{
|
2026-07-10 10:44:00 +08:00
|
|
|
|
new()
|
2026-06-30 11:03:39 +08:00
|
|
|
|
{
|
2026-07-10 10:44:00 +08:00
|
|
|
|
Exchange = JournalExchange,
|
|
|
|
|
|
Queue = PublishBookQueue,
|
|
|
|
|
|
RoutingKey = PublishBookRoutingKey,
|
|
|
|
|
|
Data = new JournalPublishBookMessage
|
|
|
|
|
|
{
|
|
|
|
|
|
BookId = id,
|
|
|
|
|
|
StartTime = book.StartTime.Value,
|
|
|
|
|
|
EndTime = book.EndTime.Value
|
|
|
|
|
|
},
|
|
|
|
|
|
BusinessType = "JournalPublish",
|
|
|
|
|
|
BusinessId = id
|
2026-06-30 11:03:39 +08:00
|
|
|
|
}
|
2026-07-10 10:44:00 +08:00
|
|
|
|
};
|
2026-06-30 11:03:39 +08:00
|
|
|
|
|
|
|
|
|
|
foreach (var page in pages)
|
|
|
|
|
|
{
|
2026-07-10 10:44:00 +08:00
|
|
|
|
publishMessages.Add(new MessagePublishInput<object>
|
2026-06-30 11:03:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
Exchange = JournalExchange,
|
|
|
|
|
|
Queue = PublishBookPageQueue,
|
|
|
|
|
|
RoutingKey = PublishBookPageRoutingKey,
|
|
|
|
|
|
Data = new JournalPublishBookPageMessage
|
|
|
|
|
|
{
|
|
|
|
|
|
BookId = id,
|
|
|
|
|
|
PageId = page.Id,
|
|
|
|
|
|
PageNo = page.PageNo,
|
2026-07-01 10:56:18 +08:00
|
|
|
|
Layout = page.Layout,
|
|
|
|
|
|
Url = DomainHelper.OssFullUrl(page.Url),
|
|
|
|
|
|
QuestionNo = questionIdsByPageId.GetValueOrDefault(page.Id) ?? string.Empty
|
2026-07-10 10:44:00 +08:00
|
|
|
|
},
|
|
|
|
|
|
BusinessType = "JournalPublish",
|
|
|
|
|
|
BusinessId = id
|
2026-06-30 11:03:39 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-10 10:44:00 +08:00
|
|
|
|
return await UseTranAsync(async () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
await messagePublishService.PublishBatchAsync(publishMessages);
|
|
|
|
|
|
return await base.Updateable()
|
|
|
|
|
|
.SetColumns(s => s.Status, JournalStatusEnum.Published)
|
|
|
|
|
|
.SetColumns(s => s.UpdatedAt, DateTime.Now)
|
|
|
|
|
|
.Where(w => w.Id == id)
|
|
|
|
|
|
.ExecuteCommandAsync() > 0;
|
|
|
|
|
|
});
|
2026-07-01 10:56:18 +08:00
|
|
|
|
|
|
|
|
|
|
static (int First, int Second, int Third, int Fourth) ParseTaskNo(string? no)
|
|
|
|
|
|
{
|
|
|
|
|
|
var parts = no?.Split('-') ?? Array.Empty<string>();
|
|
|
|
|
|
return (GetNoPart(parts, 0), GetNoPart(parts, 1), GetNoPart(parts, 2), GetNoPart(parts, 3));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int GetNoPart(string[] parts, int index)
|
|
|
|
|
|
{
|
|
|
|
|
|
return parts.Length > index && int.TryParse(parts[index], out var value) ? value : int.MaxValue;
|
|
|
|
|
|
}
|
2026-06-30 11:03:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|