refactor,feat: 批量代码重构与新增业务模块
1. 重命名枚举项与实体字段,修正类型引用 2. 新增IsNull扩展方法与多项字符串处理扩展 3. 新增大量业务DTO、服务接口与枚举定义 4. 重构RabbitMQ服务实现,替换旧版消息队列组件 5. 优化签到服务的宠物喂养事务逻辑 6. 移除冗余的项目引用与旧版消息队列代码 7. 新增Excel导出、导入模板相关工具方法
This commit is contained in:
@ -68,7 +68,7 @@ public class CheckInService(
|
||||
.Where(p => p.UserId == userId && !p.IsDeleted)
|
||||
.FirstAsync();
|
||||
|
||||
// 6. 事务执行签到相关写操作
|
||||
// 6. 事务执行签到相关写操作(含宠物喂养,统一事务)
|
||||
var result = new CheckInOutput();
|
||||
|
||||
await checkInRecordRepository.UseTranAsync(async () =>
|
||||
@ -107,6 +107,17 @@ public class CheckInService(
|
||||
Description = $"签到奖励(连续{consecutiveDays}天)"
|
||||
});
|
||||
|
||||
// 6d. 如果有活跃宠物,喂养宠物(同一事务内)
|
||||
FeedPetOutput? feedResult = null;
|
||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||
{
|
||||
feedResult = await petService.FeedPetInTranAsync(userId, new FeedPetInput
|
||||
{
|
||||
PetId = pet.Id,
|
||||
GrowthPoints = growthReward
|
||||
});
|
||||
}
|
||||
|
||||
// 构建返回结果
|
||||
result.RecordId = (long)recordId;
|
||||
result.CheckInDate = today;
|
||||
@ -116,41 +127,17 @@ public class CheckInService(
|
||||
result.PointsBalance = pointsResult.NewBalance;
|
||||
result.GrowthPointsBalance = newGrowthBalance;
|
||||
result.HasPet = pet != null;
|
||||
});
|
||||
|
||||
// 7. 如果用户有活跃宠物,调用 PetService 喂养(含进化检查),独立事务
|
||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||
{
|
||||
try
|
||||
if (feedResult != null)
|
||||
{
|
||||
var feedResult = await petService.FeedPetAsync(userId, new FeedPetInput
|
||||
{
|
||||
PetId = pet.Id,
|
||||
GrowthPoints = growthReward
|
||||
});
|
||||
|
||||
result.HasEvolved = feedResult.HasEvolved;
|
||||
result.EvolvedStageName = feedResult.EvolvedStageName;
|
||||
|
||||
logger.LogInformation("签到成长值已喂养宠物,PetId: {PetId}, 进化: {HasEvolved}",
|
||||
pet.Id, feedResult.HasEvolved);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "签到后喂养宠物失败,PetId: {PetId},将创建补偿任务", pet.Id);
|
||||
});
|
||||
|
||||
await compensationTaskService.CreateTaskAsync(new CreateCompensationTaskInput
|
||||
{
|
||||
TaskType = CompensationTaskTypeEnum.PetFeeding,
|
||||
BusinessSource = "CheckIn",
|
||||
BusinessId = result.RecordId.ToString(),
|
||||
UserId = userId,
|
||||
Payload = new { PetId = pet.Id, GrowthPoints = growthReward },
|
||||
ErrorMessage = ex.Message,
|
||||
ErrorSource = "CheckInService.CheckInAsync → PetService.FeedPetAsync",
|
||||
MaxRetries = 3
|
||||
});
|
||||
}
|
||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||
{
|
||||
logger.LogInformation("签到成长值已喂养宠物,PetId: {PetId}, 进化: {HasEvolved}",
|
||||
pet.Id, result.HasEvolved);
|
||||
}
|
||||
|
||||
logger.LogInformation("用户签到成功,UserId: {UserId}, 连续{Days}天, 积分+{Points}, 成长值+{Growth}",
|
||||
@ -339,6 +326,17 @@ public class CheckInService(
|
||||
.ExecuteCommandAsync();
|
||||
}
|
||||
|
||||
// 如果有活跃宠物,喂养宠物(同一事务内)
|
||||
FeedPetOutput? feedResult = null;
|
||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||
{
|
||||
feedResult = await petService.FeedPetInTranAsync(userId, new Models.Dto.Pet.FeedPetInput
|
||||
{
|
||||
PetId = pet.Id,
|
||||
GrowthPoints = growthReward
|
||||
});
|
||||
}
|
||||
|
||||
result.RecordId = (long)recordId;
|
||||
result.CheckInDate = targetDate;
|
||||
result.ConsecutiveDays = 0;
|
||||
@ -347,27 +345,17 @@ public class CheckInService(
|
||||
result.PointsBalance = pointsResult.NewBalance;
|
||||
result.GrowthPointsBalance = newGrowthBalance;
|
||||
result.HasPet = pet != null;
|
||||
});
|
||||
|
||||
// 如果有活跃宠物,喂养成长值
|
||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||
{
|
||||
try
|
||||
if (feedResult != null)
|
||||
{
|
||||
var feedResult = await petService.FeedPetAsync(userId, new Models.Dto.Pet.FeedPetInput
|
||||
{
|
||||
PetId = pet.Id,
|
||||
GrowthPoints = growthReward
|
||||
});
|
||||
|
||||
result.HasEvolved = feedResult.HasEvolved;
|
||||
result.EvolvedStageName = feedResult.EvolvedStageName;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
logger.LogWarning(ex, "补签后喂养宠物失败,PetId: {PetId}", pet.Id);
|
||||
// 补偿机制:如需可在此创建补偿任务
|
||||
}
|
||||
});
|
||||
|
||||
if (pet != null && pet.Status == (int)UserPetStatusEnum.Active && growthReward > 0)
|
||||
{
|
||||
logger.LogInformation("补签成长值已喂养宠物,PetId: {PetId}, 进化: {HasEvolved}",
|
||||
pet.Id, result.HasEvolved);
|
||||
}
|
||||
|
||||
logger.LogInformation("补签成功,UserId: {UserId}, Date: {Date}, 积分+{Points}, 成长值+{Growth}",
|
||||
|
||||
526
QYZH.InteractiveMagazine.Service/JournalCatalogService.cs
Normal file
526
QYZH.InteractiveMagazine.Service/JournalCatalogService.cs
Normal file
@ -0,0 +1,526 @@
|
||||
|
||||
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;
|
||||
//}
|
||||
}
|
||||
211
QYZH.InteractiveMagazine.Service/JournalPageService.cs
Normal file
211
QYZH.InteractiveMagazine.Service/JournalPageService.cs
Normal file
@ -0,0 +1,211 @@
|
||||
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Newtonsoft.Json;
|
||||
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.Journal;
|
||||
using QYZH.InteractiveMagazine.Models.Entity;
|
||||
using QYZH.InteractiveMagazine.Models.Enum;
|
||||
using QYZH.InteractiveMagazine.Repository;
|
||||
using System.Diagnostics;
|
||||
using Yitter.IdGenerator;
|
||||
|
||||
namespace QYZH.InteractiveMagazine.Service;
|
||||
|
||||
public class JournalPageService(BaseRepository<Journal> JournalRepository,
|
||||
OssService ossService,
|
||||
IHttpClientFactory httpClientFactory,
|
||||
IConfiguration configuration,
|
||||
IRabbitMQService rabbitMqService,
|
||||
BaseRepository<JournalPageTask> JournalPageTaskRepository) : BaseRepository<JournalPage>, IJournalPageService
|
||||
{
|
||||
|
||||
public async Task<long> InsertAsync(JournalAddV2Input input)
|
||||
{
|
||||
|
||||
var Journal = await JournalRepository.Queryable().Where(w => w.Id == input.JournalId).FirstAsync();
|
||||
BusinessException.ThrowIf(Journal.IsNull(), "未找到书本");
|
||||
|
||||
BusinessException.ThrowIf(Journal?.Status == (int)JournalStatusEnum.Archive, "书籍已归档不能修改");
|
||||
|
||||
var pages = new List<JournalPage>();
|
||||
//var dotMatrixpages = new List<DotMatrixPage>();
|
||||
|
||||
//var dotMatrixpage = new DotMatrixPage()
|
||||
//{
|
||||
// Id = YitIdHelper.NextId(),
|
||||
// DotMatrixNoteJournalId = input.JournalId,
|
||||
// WidthMilliMeter = Journal.Width,
|
||||
// HightMilliMeter = Journal.Height,
|
||||
//};
|
||||
|
||||
var pageTemp = new JournalPage()
|
||||
{
|
||||
JournalId = input.JournalId,
|
||||
JournalCatalogId = input.JournalCatalogId,
|
||||
//DotMatrixPageId = dotMatrixpage.Id,
|
||||
Url = input.Url,
|
||||
PageNum = input.PageNum,
|
||||
};
|
||||
|
||||
//dotMatrixpages.Add(dotMatrixpage);
|
||||
pages.Add(pageTemp);
|
||||
|
||||
await UseTranAsync(async () =>
|
||||
{
|
||||
//using var uow = Context.Ado.BeginTran();
|
||||
var pageData = await base.InsertRangeAsync(pages);
|
||||
BusinessException.ThrowIf(pageData.IsNull(), "创建页失败");
|
||||
|
||||
//var result = await dotMatrixPageRepository.InsertRangeAsync(dotMatrixpages);
|
||||
//BusinessException.ThrowIf(result, "创建点阵页失败");
|
||||
});
|
||||
|
||||
return pages.Count; // 返回主目录ID
|
||||
}
|
||||
|
||||
public async Task<bool> UpdateAsync(PageLayoutInput input)
|
||||
{
|
||||
var page = await Queryable().Where(w => w.Id == input.Id).FirstAsync();
|
||||
BusinessException.ThrowIf(page == null, "不存在此页");
|
||||
|
||||
var Journal = await JournalRepository.GetByIdAsync(page.JournalId);
|
||||
BusinessException.ThrowIf(Journal == null, "不存在此书");
|
||||
|
||||
BusinessException.ThrowIf(Journal?.Status == (int)JournalStatusEnum.Archive, "书籍已归档不能修改");
|
||||
var transResult = await UseTranAsync(async () =>
|
||||
{
|
||||
//await dotMatrixPageRepository.Updateable().SetColumns(s => s.Area == input.Layout)
|
||||
// .SetColumns(s => s.AreaPoints == dotMatrixPage.Area)
|
||||
// .SetColumns(s => s.WidthMilliMeter == dotMatrixPage.WidthMilliMeter)
|
||||
// .SetColumns(s => s.HightMilliMeter == dotMatrixPage.HightMilliMeter)
|
||||
// .SetColumns(s => s.WidthDotMatrix == dotMatrixPage.WidthDotMatrix)
|
||||
// .SetColumns(s => s.HightDotMatrix == dotMatrixPage.HightDotMatrix)
|
||||
// .Where(w => w.Id == page.DotMatrixPageId).ExecuteCommandAsync();
|
||||
|
||||
page.Layout = input.Layout;
|
||||
base.Update(page);
|
||||
|
||||
input.TasksImages?.ForEach(it =>
|
||||
{
|
||||
var key = $"Journal/{Journal.Id}/{input.Id}/{it.TaskId}/qustion.{it.Url.ToExtension()}";
|
||||
ossService.CopyObject(it.Url.RemoveDomain(), key);
|
||||
JournalPageTaskRepository.Updateable().SetColumns(s => s.TaskUrl, key).Where(w => w.Id == it.TaskId).ExecuteCommand();
|
||||
});
|
||||
return true;
|
||||
});
|
||||
return transResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修改书页的点阵码
|
||||
/// </summary>
|
||||
/// <param name="JournalId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> UpdatePageNoAsync(long JournalId)
|
||||
{
|
||||
//这里不能修改 Exchange = "icr.direct" 否则会报错
|
||||
var msRes = await rabbitMqService.SendAsync(new RabbitMQSendParam { Exchange = "icr.direct", Queue = "mq.Journal.updatePageNo.queue", RoutingKey = "mq.Journal.updatePageNo", Data = JournalId });//发生消息
|
||||
return msRes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 打印书页(全部)
|
||||
/// </summary>
|
||||
/// <param name="JournalId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> PrintJournalPageAsync(long JournalId)
|
||||
{
|
||||
//这里不能修改 Exchange = "icr.direct" 否则会报错
|
||||
var msRes = await rabbitMqService.SendAsync(new RabbitMQSendParam { Exchange = "icr.direct", Queue = "mq.Journal.printJournalPage.queue", RoutingKey = "mq.Journal.printJournalPage", Data = JournalId });//发生消息
|
||||
return msRes;
|
||||
}
|
||||
|
||||
public async Task<JournalPageV2Output> DetailAsync(long id)
|
||||
{
|
||||
var output = await Queryable()
|
||||
//.LeftJoin<DotMatrixPage>((a, b) => a.DotMatrixPageId == b.Id)
|
||||
.Where(a => a.Id == id)
|
||||
.Select(a => new JournalPageV2Output
|
||||
{
|
||||
JournalId = a.JournalId,
|
||||
JournalCatalogId = a.JournalCatalogId,
|
||||
JournalPageId = a.Id,
|
||||
Url = a.Url,
|
||||
Layout = a.Layout,
|
||||
PageNum = a.PageNum,
|
||||
})
|
||||
.FirstAsync();
|
||||
|
||||
if (output != null)
|
||||
{
|
||||
output.Tasks = await JournalPageTaskRepository.Queryable()//.Select<IcrJournalAssignTask>()
|
||||
.Where(a => a.JournalPageId == output.JournalPageId)
|
||||
.Select(a => new JournalPageTaskV2Output
|
||||
{
|
||||
Id = a.Id,
|
||||
No = a.No,
|
||||
Type = (TaskBankTypeEnum)a.Type,
|
||||
Options = a.Options,
|
||||
Answers = a.Answer,
|
||||
Analysis = a.Analysis,
|
||||
//Assign = SqlFunc.Subqueryable<IcrJournalAssignTask>().Where(m => a.Id == m.JournalPageTaskId).Any()
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
//if (output?.Areas != null)
|
||||
// output.Areas = await _JournalPageOtherRepository.Queryable().Where(w => w.JournalPageId == output.JournalPageId).Select<JournalPageOtherOutput>().ToListAsync();
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(long id)
|
||||
{
|
||||
return await Deleteable().Where(d => d.Id == id).ExecuteCommandAsync() > 0;
|
||||
}
|
||||
//public async Task<List<JournalPageNoArticleOutput>> PageNoArticleAsync(long JournalId)
|
||||
//{
|
||||
// return await Queryable().Where(d => d.JournalId == JournalId)
|
||||
// .Where(w => w.JournalArticleId == null)
|
||||
// .ToListAsync(x => new JournalPageNoArticleOutput()
|
||||
// {
|
||||
// Id = x.Id,
|
||||
// PageUrl = x.Url,
|
||||
// PageNum = x.PageNum
|
||||
// });
|
||||
//}
|
||||
|
||||
private int MillimeterToDotMatrixUnit(float millimeterValue)
|
||||
{
|
||||
return (int)(millimeterValue * 8 / 0.3);
|
||||
}
|
||||
}
|
||||
|
||||
public class JournalDotPage_Task
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int W { get; set; }
|
||||
public int H { get; set; }
|
||||
}
|
||||
|
||||
public class JournalDotPage_Answer
|
||||
{
|
||||
public int X { get; set; }
|
||||
public int Y { get; set; }
|
||||
public int W { get; set; }
|
||||
public int H { get; set; }
|
||||
}
|
||||
|
||||
public class JournalDotPage_Area
|
||||
{
|
||||
public JournalDotPage_Task Task { get; set; }
|
||||
|
||||
public List<JournalDotPage_Answer> AnswerList { get; set; }
|
||||
|
||||
public long TaskId { get; set; }
|
||||
|
||||
}
|
||||
176
QYZH.InteractiveMagazine.Service/JournalPageTaskService.cs
Normal file
176
QYZH.InteractiveMagazine.Service/JournalPageTaskService.cs
Normal file
@ -0,0 +1,176 @@
|
||||
using Mapster;
|
||||
using QYZH.InteractiveMagazine.Common.Extensions;
|
||||
using QYZH.InteractiveMagazine.Infrastructure.OSS;
|
||||
using QYZH.InteractiveMagazine.IService;
|
||||
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 JournalPageTaskService(BaseRepository<Journal> journalRepository, OssService ossService) : BaseRepository<JournalPageTask>, IJournalPageTaskService
|
||||
{
|
||||
public async Task<long?> InsertAsync(JournalPageTaskAddInput input)
|
||||
{
|
||||
var JournalStatus = await journalRepository.Queryable().Where(w => w.Id == input.JournalId).Select(s => s.Status).FirstAsync();
|
||||
BusinessException.ThrowIf(JournalStatus == (int)JournalStatusEnum.Archive, "书籍已归档不能添加题目");
|
||||
|
||||
var map = input.Adapt<JournalPageTask>();
|
||||
map.Id = YitIdHelper.NextId();
|
||||
map.GroupId = map.Id;
|
||||
var no = input.No.Split('-').Select(int.Parse).ToArray();
|
||||
if (no.Last() >= 2)
|
||||
{
|
||||
var startNo = $"{no[0]}-{no[1]}-{no[2]}-1";
|
||||
var groupTask = await Queryable().Where(w => w.No == startNo && w.JournalId == input.JournalId).FirstAsync();
|
||||
BusinessException.ThrowIf(groupTask.IsNull(), "未找到关联跨页的第一部分");
|
||||
map.GroupId = groupTask.GroupId;
|
||||
map.Score = groupTask.Score;
|
||||
map.AnswerTime = groupTask.AnswerTime;
|
||||
map.Type = groupTask.Type;
|
||||
}
|
||||
|
||||
var data = await base.InsertAsync(map);
|
||||
return map?.Id;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> UpdateAsync(JournalPageTaskUpdateInput input)
|
||||
{
|
||||
var JournalStatus = await journalRepository.Queryable().Where(w => w.Id == input.JournalId).Select(s => s.Status).FirstAsync();
|
||||
BusinessException.ThrowIf(JournalStatus == (int)JournalStatusEnum.Archive, "书籍已归档不能修改题目");
|
||||
|
||||
var task = await base.Queryable().Where(w => w.Id == input.Id).FirstAsync();
|
||||
BusinessException.ThrowIf(task.IsNull(), "未找到关联的题号");
|
||||
|
||||
var sameTask = await base.Queryable().Where(w => w.No == input.No && w.JournalId == input.JournalId).FirstAsync();
|
||||
BusinessException.ThrowIf(sameTask.IsNotEmpty(), "已经存在相同题号题目");
|
||||
|
||||
|
||||
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{task.GroupId}";
|
||||
//if (task.AnswerUrl != input.AnswerUrl && input.AnswerUrl.NotNull())
|
||||
//{
|
||||
// ossService.CopyObject(input.AnswerUrl.RemoveDomain(), $"{key}/answer.{input.AnswerUrl.ToExtension()}");
|
||||
// input.AnswerUrl = $"{key}/answer.{input.AnswerUrl.ToExtension()}";
|
||||
//}
|
||||
//if (task.AnalysisUrl != input.AnalysisUrl && input.AnalysisUrl.NotNull())
|
||||
//{
|
||||
// ossService.CopyObject(input.AnalysisUrl.RemoveDomain(), $"{key}/analysis.{input.AnalysisUrl.ToExtension()}");
|
||||
// input.AnalysisUrl = $"{key}/analysis.{input.AnalysisUrl.ToExtension()}";
|
||||
//}
|
||||
//if (task.VideoUrl != input.VideoUrl && input.VideoUrl.NotNull())
|
||||
//{
|
||||
// ossService.CopyObject(input.VideoUrl.RemoveDomain(), $"{key}/video.{input.VideoUrl.ToExtension()}");
|
||||
// input.VideoUrl = $"{key}/video.{input.VideoUrl.ToExtension()}";
|
||||
//}
|
||||
|
||||
input.Task = Deal(task.Task, input.Task, key, "task");
|
||||
input.Answer = Deal(task.Answer, input.Answer, key, "answer");
|
||||
input.Analysis = Deal(task.Analysis, input.Analysis, key, "analysis");
|
||||
|
||||
task.Type = input.Type;
|
||||
task.Task = input.Task;
|
||||
task.Options = input.Options;
|
||||
task.Answer = input.Answer;
|
||||
//task.AnswerUrl = input.AnswerUrl;
|
||||
task.Analysis = input.Analysis;
|
||||
//task.AnalysisUrl = input.AnalysisUrl;
|
||||
task.Score = input.Score;
|
||||
task.AnswerTime = input.AnswerTime;
|
||||
// task.VideoUrl = input.VideoUrl;
|
||||
task.No = input.No;
|
||||
var no = input.No.Split('-').Select(int.Parse).ToArray();
|
||||
if (no.Last() >= 2)
|
||||
{
|
||||
var startNo = $"{no[0]}-{no[1]}-{no[2]}-1";
|
||||
var groupTask = await Queryable().Where(w => w.No == startNo && w.JournalId == input.JournalId).FirstAsync();
|
||||
BusinessException.ThrowIf(groupTask.IsNull(), "未找到关联跨页的第一部分");
|
||||
task.GroupId = groupTask.GroupId;
|
||||
task.Score = groupTask.Score;
|
||||
task.AnswerTime = groupTask.AnswerTime;
|
||||
task.Type = groupTask.Type;
|
||||
}
|
||||
//task.JournalId = input.JournalId;
|
||||
//task.JournalPageId = input.JournalPageId;
|
||||
// 这里不能直接用Set,因为jsonmap无法赋值 不知道为啥
|
||||
return await base.Updateable(task).ExecuteCommandAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<bool> KeywordAnalysisAsync(JournalPageTaskKeywordAnalysis input)
|
||||
{
|
||||
return await base.Updateable().SetColumns(s => s.KeywordAnalysis, input.KeywordAnalysis).Where(w => w.GroupId == input.Id).ExecuteCommandAsync() > 0;
|
||||
}
|
||||
|
||||
public async Task<JournalPageTaskOutput> DetailAsync(long id)
|
||||
{
|
||||
var data = await base.Queryable()
|
||||
.Where(w => w.Id == id)
|
||||
.Select(w => new JournalPageTaskOutput()
|
||||
, true).FirstAsync();
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteAsync(long id)
|
||||
{
|
||||
var task = await base.Queryable().Where(w => w.Id == id).FirstAsync();
|
||||
BusinessException.ThrowIf(task.IsNull(), "问题不存在");
|
||||
|
||||
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{id}";
|
||||
|
||||
ossService.DeleteObject(key);
|
||||
|
||||
return await base.DeleteAsync(d => d.Id == id);
|
||||
}
|
||||
|
||||
public async Task<bool> ComplementAsync(JournalPageTaskComplementInput input)
|
||||
{
|
||||
var task = await base.Queryable().Where(w => w.Id == input.Id).FirstAsync();
|
||||
BusinessException.ThrowIf(task.IsNull(), "问题不存在");
|
||||
|
||||
if (input.AudioUrl.NotNull())
|
||||
{
|
||||
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{task.Id}/audio.{input.AudioUrl.ToExtension()}";
|
||||
ossService.CopyObject(input.AudioUrl, key);
|
||||
input.AudioUrl = key;
|
||||
}
|
||||
if (input.PointsUrl.NotNull())
|
||||
{
|
||||
var key = $"Journal/{task.JournalId}/{task.JournalPageId}/{task.Id}/points.json";
|
||||
ossService.CopyObject(input.PointsUrl, key);
|
||||
input.PointsUrl = key;
|
||||
}
|
||||
return await Updateable(input.Adapt<JournalPageTask>()).Where(w => w.Id == input.Id).ExecuteCommandAsync() > 0;
|
||||
}
|
||||
|
||||
private string Deal(string oldUrls, string newUrls, string key, string type)
|
||||
{
|
||||
// 如果 newUrls 为空,则直接返回
|
||||
if (newUrls.IsNull()) return null;
|
||||
|
||||
// 处理 oldUrls 为 null 的情况,将其视为空字符串
|
||||
var oldTaskUrls = oldUrls.AllUrl().Distinct();
|
||||
var taskUrls = newUrls.AllUrl().Distinct().Select(s => s.RemoveDomain());
|
||||
|
||||
// 删除不存在的(存在于旧集合但不在新集合中)
|
||||
var removedUrls = oldTaskUrls.Except(taskUrls).ToList();
|
||||
ossService.DeleteObjects(removedUrls);
|
||||
|
||||
// 添加新增的(存在于新集合但不在旧集合中)
|
||||
var addedUrls = taskUrls.Except(oldTaskUrls).ToList();
|
||||
foreach (var item in addedUrls)
|
||||
{
|
||||
var value = $"{key}/{type}/{YitIdHelper.NextId()}.{item.ToExtension()}";
|
||||
ossService.CopyObject(item, value);
|
||||
|
||||
newUrls = newUrls.Replace(item, value);
|
||||
|
||||
}
|
||||
return newUrls;
|
||||
}
|
||||
}
|
||||
313
QYZH.InteractiveMagazine.Service/JournalService.cs
Normal file
313
QYZH.InteractiveMagazine.Service/JournalService.cs
Normal file
@ -0,0 +1,313 @@
|
||||
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></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
|
||||
};
|
||||
|
||||
|
||||
//铺码业务后续调整
|
||||
|
||||
//var exist = pages.All(c => c.PageNo.IsNull());
|
||||
//if (!exist)
|
||||
//{
|
||||
// var dotIds = pages.Select(s => s.DotId).Distinct();
|
||||
// var dots = await dotFileRepository.Queryable().Where(w => dotIds.Contains(w.Id)).ToListAsync();
|
||||
// output.DotMatrixs = dots.Select(s => new DotMatrixMqOutput() { DotId = s.Id, FileAddress = s.FileAddress }).ToList();
|
||||
// foreach (var page in pages)
|
||||
// {
|
||||
// var find = output.DotMatrixs.Find(f => f.DotId == page.DotId);
|
||||
// find!.Pages.Add(new DotMatrixPageDto()
|
||||
// {
|
||||
// PageNo = page.PageNo,
|
||||
// PageNum = 1
|
||||
// });
|
||||
// }
|
||||
|
||||
// var ok = await producingService.SendAsync(new RabbitMQSendParam() { Exchange = "icr.direct", RoutingKey = "mq.dotmatrix", Data = output });
|
||||
// BusinessException.ThrowIf(!ok, "消息发送失败,生成失败");
|
||||
|
||||
// await base.Updateable().SetColumns(s => s.Status, JournalStatusEnum.Codeing).Where(w => w.Id == id).ExecuteCommandAsync();
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// //var first = await dotFileDetailRepository.Queryable().Where(w => w.IsUse == false).GroupBy(g => g.DotId).Having(h => h.Count() >= pages.Count).FirstAsync(a => new { DotId = a.Key, Count = a.Count() });
|
||||
// var first = await dotFileDetailRepository.Queryable().Where(w => w.IsUse == false).GroupBy(g => g.DotId).Having(h => SqlFunc.AggregateCount(h.Id) >= pages.Count).Select(a => new { DotId = a.DotId, Count = SqlFunc.AggregateCount(a.Id) }).FirstAsync();
|
||||
// BusinessException.ThrowIf(first.IsNull(), "点阵页码不足,生成失败");
|
||||
// var dotFile = await dotFileRepository.Queryable().Where(w => w.Id == first.DotId).FirstAsync();
|
||||
// if (dotFile != null)
|
||||
// dotFile.Details = dotFileDetailRepository.Queryable().Where(w => !w.IsUse && w.DotId == dotFile.Id).Take(pages.Count()).ToList();
|
||||
// var dotMatrix = new DotMatrixMqOutput()
|
||||
// {
|
||||
// DotId = dotFile.Id,
|
||||
// FileAddress = dotFile.FileAddress,
|
||||
// };
|
||||
// output.DotMatrixs.Add(dotMatrix);
|
||||
|
||||
// foreach (var item in dotFile.Details)
|
||||
// {
|
||||
// var index = dotFile.Details.IndexOf(item);
|
||||
// pages[index].PageNo = item.PageName;
|
||||
// pages[index].DotId = dotFile.Id;
|
||||
|
||||
// dotMatrix.Pages.Add(new DotMatrixPageDto()
|
||||
// {
|
||||
// PageNo = item.PageName,
|
||||
// PageNum = 1
|
||||
// });
|
||||
// }
|
||||
// await UseTranAsync(async () =>
|
||||
// {
|
||||
// // 更新使用数量
|
||||
// //await dotFileRepository.Updateable().SetColumns(s => s.TotalUse, dotFile.TotalUse + pages.Count).Where(w => w.Id == dotFile.Id).ExecuteCommandAsync();
|
||||
// // 更新使用
|
||||
// await dotFileDetailRepository.Updateable().SetColumns(s => s.IsUse, true).Where(w => dotFile.Details.Select(s => s.Id).Contains(w.Id)).ExecuteCommandAsync();
|
||||
// // 更新页码
|
||||
// await dotMatrixPageRepository.UpdateRangeAsync(pages);
|
||||
// await Updateable().SetColumns(s => s.Status, JournalStatusEnum.CodeSuccess).Where(w => w.Id == id).ExecuteCommandAsync();
|
||||
// //发送消息
|
||||
// //var ok = await producingService.SendAsync(new RabbitMQSendParam() { Exchange = "icr.direct", RoutingKey = "mq.dotmatrix", Data = output });
|
||||
// //BusinessException.ThrowIf(!ok, "消息发送失败,生成失败");
|
||||
// });
|
||||
//}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@ -253,79 +253,103 @@ public class PetService(
|
||||
throw new BusinessException("宠物未激活,无法喂养", 400);
|
||||
}
|
||||
|
||||
// 事务保证一致性
|
||||
await UseTranAsync(async () =>
|
||||
{
|
||||
result = await FeedPetInTranAsync(userId, input);
|
||||
});
|
||||
|
||||
logger.LogInformation("喂养宠物成功,PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
|
||||
input.PetId, result.GrowthBefore, result.GrowthAfter, result.HasEvolved);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 喂养宠物(无事务,需在外部事务中调用)
|
||||
/// </summary>
|
||||
public async Task<FeedPetOutput> FeedPetInTranAsync(long userId, FeedPetInput input)
|
||||
{
|
||||
// 查询宠物
|
||||
var pet = await petRepository.GetByIdAsync(input.PetId);
|
||||
if (pet == null || pet.IsDeleted)
|
||||
throw new BusinessException("宠物不存在", 404);
|
||||
|
||||
if (pet.UserId != userId)
|
||||
throw new BusinessException("无权操作该宠物", 403);
|
||||
|
||||
if (pet.Status != (int)UserPetStatusEnum.Active)
|
||||
throw new BusinessException("宠物未激活,无法喂养", 400);
|
||||
|
||||
var growthBefore = pet.GrowthPoints;
|
||||
var growthAfter = growthBefore + input.GrowthPoints;
|
||||
var hasEvolved = false;
|
||||
string? evolvedStageName = null;
|
||||
|
||||
// 事务保证一致性
|
||||
await UseTranAsync(async () =>
|
||||
// 累加成长值和喂养次数
|
||||
var updateResult = await petRepository.Context.Updateable<UserPet>()
|
||||
.SetColumns(p => p.GrowthPoints == growthAfter)
|
||||
.SetColumns(p => p.FeedingCount == p.FeedingCount + 1)
|
||||
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
||||
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
||||
.Where(p => p.Id == input.PetId)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
if (updateResult <= 0)
|
||||
{
|
||||
// 累加成长值和喂养次数
|
||||
var updateResult = await petRepository.Context.Updateable<UserPet>()
|
||||
.SetColumns(p => p.GrowthPoints == growthAfter)
|
||||
.SetColumns(p => p.FeedingCount == p.FeedingCount + 1)
|
||||
throw new BusinessException("更新宠物成长值失败", 500);
|
||||
}
|
||||
|
||||
// 进化检查:查找下一阶段进化形态(PreviousEvolutionId 类型为 long?)
|
||||
var nextEvolution = await petEvolutionRepository.Queryable()
|
||||
.Where(e => e.PreviousEvolutionId == pet.CurrentEvolutionId
|
||||
&& e.RequiredGrowth <= growthAfter
|
||||
&& e.Status == (int)DefaultStatusEnum.Active)
|
||||
.OrderBy(e => e.RequiredGrowth, OrderByType.Desc)
|
||||
.FirstAsync();
|
||||
|
||||
if (nextEvolution != null)
|
||||
{
|
||||
// 触发进化
|
||||
var evolveResult = await petRepository.Context.Updateable<UserPet>()
|
||||
.SetColumns(p => p.CurrentEvolutionId == nextEvolution.Id)
|
||||
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
||||
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
||||
.Where(p => p.Id == input.PetId)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
if (updateResult <= 0)
|
||||
if (evolveResult > 0)
|
||||
{
|
||||
throw new BusinessException("更新宠物成长值失败", 500);
|
||||
hasEvolved = true;
|
||||
evolvedStageName = nextEvolution.StageName;
|
||||
logger.LogInformation("宠物进化成功,PetId: {PetId}, 新形态: {StageName} (Level {StageLevel})",
|
||||
input.PetId, nextEvolution.StageName, nextEvolution.StageLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// 进化检查:查找下一阶段进化形态(PreviousEvolutionId 类型为 long?)
|
||||
var nextEvolution = await petEvolutionRepository.Queryable()
|
||||
.Where(e => e.PreviousEvolutionId == pet.CurrentEvolutionId
|
||||
&& e.RequiredGrowth <= growthAfter
|
||||
&& e.Status == (int)DefaultStatusEnum.Active)
|
||||
.OrderBy(e => e.RequiredGrowth, OrderByType.Desc)
|
||||
.FirstAsync();
|
||||
// 写入喂养记录
|
||||
var record = new PetFeedingRecord
|
||||
{
|
||||
PetId = input.PetId,
|
||||
UserId = userId,
|
||||
PointsUsed = 0, // 预留:后期可扩展为消耗积分喂养
|
||||
GrowthChange = input.GrowthPoints,
|
||||
GrowthBefore = growthBefore,
|
||||
GrowthAfter = growthAfter,
|
||||
Type = PetFeedingRecordTypeEnum.Normal,
|
||||
Status = (int)PetFeedingRecordStatusEnum.Success,
|
||||
IsDeleted = false,
|
||||
CreatedBy = userId.ToString(),
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = userId.ToString(),
|
||||
UpdatedAt = DateTime.Now
|
||||
};
|
||||
|
||||
if (nextEvolution != null)
|
||||
{
|
||||
// 触发进化
|
||||
var evolveResult = await petRepository.Context.Updateable<UserPet>()
|
||||
.SetColumns(p => p.CurrentEvolutionId == nextEvolution.Id)
|
||||
.SetColumns(p => p.UpdatedAt == DateTime.Now)
|
||||
.SetColumns(p => p.UpdatedBy == userId.ToString())
|
||||
.Where(p => p.Id == input.PetId)
|
||||
.ExecuteCommandAsync();
|
||||
|
||||
if (evolveResult > 0)
|
||||
{
|
||||
hasEvolved = true;
|
||||
evolvedStageName = nextEvolution.StageName;
|
||||
logger.LogInformation("宠物进化成功,PetId: {PetId}, 新形态: {StageName} (Level {StageLevel})",
|
||||
input.PetId, nextEvolution.StageName, nextEvolution.StageLevel);
|
||||
}
|
||||
}
|
||||
|
||||
// 写入喂养记录
|
||||
var record = new PetFeedingRecord
|
||||
{
|
||||
PetId = input.PetId,
|
||||
UserId = userId,
|
||||
PointsUsed = 0, // 预留:后期可扩展为消耗积分喂养
|
||||
GrowthChange = input.GrowthPoints,
|
||||
GrowthBefore = growthBefore,
|
||||
GrowthAfter = growthAfter,
|
||||
Type = PetFeedingRecordTypeEnum.Normal,
|
||||
Status = (int)PetFeedingRecordStatusEnum.Success,
|
||||
IsDeleted = false,
|
||||
CreatedBy = userId.ToString(),
|
||||
CreatedAt = DateTime.Now,
|
||||
UpdatedBy = userId.ToString(),
|
||||
UpdatedAt = DateTime.Now
|
||||
};
|
||||
|
||||
var insertResult = await feedingRecordRepository.InsertAsync(record);
|
||||
if (!insertResult)
|
||||
{
|
||||
throw new BusinessException("写入喂养记录失败", 500);
|
||||
}
|
||||
});
|
||||
var insertResult = await feedingRecordRepository.InsertAsync(record);
|
||||
if (!insertResult)
|
||||
{
|
||||
throw new BusinessException("写入喂养记录失败", 500);
|
||||
}
|
||||
|
||||
logger.LogInformation("喂养宠物成功,PetId: {PetId}, 成长值: {Before} -> {After}, 进化: {HasEvolved}",
|
||||
input.PetId, growthBefore, growthAfter, hasEvolved);
|
||||
|
||||
@ -158,7 +158,7 @@ public class UsersService(
|
||||
if (journalDict.TryGetValue(item.JournalId, out var journal))
|
||||
{
|
||||
item.JournalTitle = journal.Title;
|
||||
item.CoverImageUrl = journal.CoverImageUrl;
|
||||
item.CoverImageUrl = journal.Cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user