Files
QYZH.InteractiveMagazine/QYZH.InteractiveMagazine.Service/JournalService.cs
glz f2f7ec9100 feat: 完成多模块功能迭代与代码重构
本次提交包含多方面更新:
1. 新增消息类型枚举、杂志标题字段与AI提示词状态管理
2. 重构杂志服务,新增创建杂志接口并统一OSS路径命名规范
3. 调整题库任务结构,将答案解析迁移至独立表并优化关联查询
4. 新增AI提示词无分页查询与状态切换接口
5. 精简任务添加输入DTO,移除冗余字段
6. 修复控制器路由命名大小写不一致问题
2026-06-16 16:54:02 +08:00

306 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<JournalPage> JournalPageRepository,
BaseRepository<JournalPageTask> JournalPageTaskRepository,
BaseRepository<DotFile> dotFileRepository,
BaseRepository<DotFileDetail> dotFileDetailRepository, OssService ossService, ILogger<JournalService> logger) : BaseRepository<Journal>, IJournalService
{
/// <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);
}
/// <summary>
/// 创建杂志
/// </summary>
/// <param name="input">杂志信息</param>
/// <returns>新杂志ID</returns>
public async Task<BaseResponse<long>> AddAsync(JournalAddDto input)
{
BusinessException.ThrowIf(string.IsNullOrWhiteSpace(input.Name), "书籍名称不能为空");
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("创建失败");
}
logger.LogInformation("杂志创建成功ID: {Id}, 名称: {Name}", journal.Id, input.Name);
return BaseResponse<long>.Success(journal.Id);
}
/// <summary>
/// 编辑
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
public async Task<BaseResponse<bool>> EditAsync(JournalEditDto input)
{
var Journal = await base.GetByIdAsync(input.Id);
BusinessException.ThrowIf(Journal.IsNull(), "不存在的Id");
BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Archive, "已归档不可编辑");
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<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)
{
BusinessException.ThrowIf(!base.Queryable().Any(w => ids.Contains(w.Id)), "ID不存在");
BusinessException.ThrowIf(base.Queryable().Any(w => ids.Contains(w.Id) && w.Status == (int)JournalStatusEnum.Archive), "已归档不可删除");
var result = await UseTranAsync(async () =>
{
await base.DeleteAsync(d => ids.Contains(d.Id));
await JournalPageRepository.DeleteAsync(d => ids.Contains(d.JournalId));
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;
}
/// <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();
BusinessException.ThrowIf(Journal.IsNull(), "不存在书");
BusinessException.ThrowIf(Journal.Status == (int)JournalStatusEnum.Codeing, "正在生成中...");
var pages = await JournalPageRepository.Queryable().Where(w => w.JournalId == id).ToListAsync();
BusinessException.ThrowIf(pages.IsNull(), "不存在页");
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();
BusinessException.ThrowIf(tasks.Count == 0 && status == JournalStatusEnum.Archive, "未添加任何题目,无法归档");
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())}未保存,无法归档");
var res = await base.Updateable().SetColumns(s => s.Status, status).Where(w => w.Id == id).ExecuteCommandAsync() > 0;
return res;
}
}