mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
[skip ci] uigf 4 support part 3
This commit is contained in:
@@ -9,7 +9,7 @@ namespace Snap.Hutao.Extension;
|
||||
internal static class CollectionExtension
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this Collection<TSource>? source)
|
||||
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this ICollection<TSource>? source)
|
||||
{
|
||||
if (source is { Count: > 0 })
|
||||
{
|
||||
|
||||
@@ -67,17 +67,6 @@ internal static class ListExtension
|
||||
return list.GetRange(start, length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrEmpty<TSource>([NotNullWhen(false)][MaybeNullWhen(true)] this List<TSource>? source)
|
||||
{
|
||||
if (source is { Count: > 0 })
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void RemoveLast<T>(this IList<T> collection)
|
||||
{
|
||||
collection.RemoveAt(collection.Count - 1);
|
||||
|
||||
@@ -8,11 +8,17 @@ namespace Snap.Hutao.Model.Entity.Abstraction;
|
||||
internal interface IDbMappingForeignKeyFrom<TSource, TFrom>
|
||||
{
|
||||
[Pure]
|
||||
static abstract TSource From(in Guid foreignKey, in TFrom from);
|
||||
static abstract TSource From(Guid foreignKey, TFrom from);
|
||||
}
|
||||
|
||||
internal interface IDbMappingForeignKeyFrom<TSource, T1, T2>
|
||||
{
|
||||
[Pure]
|
||||
static abstract TSource From(in Guid foreignKey, in T1 param1, in T2 param2);
|
||||
static abstract TSource From(Guid foreignKey, T1 param1, T2 param2);
|
||||
}
|
||||
|
||||
internal interface IDbMappingForeignKeyFrom<TSource, T1, T2, T3>
|
||||
{
|
||||
[Pure]
|
||||
static abstract TSource From(Guid foreignKey, T1 param1, T2 param2, T3 param3);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity.Abstraction;
|
||||
using Snap.Hutao.Model.InterChange;
|
||||
using Snap.Hutao.Model.InterChange.Achievement;
|
||||
using Snap.Hutao.Model.Intrinsic;
|
||||
using Snap.Hutao.Model.Primitive;
|
||||
@@ -14,7 +15,8 @@ namespace Snap.Hutao.Model.Entity;
|
||||
internal sealed class Achievement : IAppDbEntityHasArchive,
|
||||
IEquatable<Achievement>,
|
||||
IDbMappingForeignKeyFrom<Achievement, AchievementId>,
|
||||
IDbMappingForeignKeyFrom<Achievement, UIAFItem>
|
||||
IDbMappingForeignKeyFrom<Achievement, UIAFItem>,
|
||||
IDbMappingForeignKeyFrom<Achievement, HutaoReservedAchievement>
|
||||
{
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
@@ -33,7 +35,7 @@ internal sealed class Achievement : IAppDbEntityHasArchive,
|
||||
|
||||
public AchievementStatus Status { get; set; }
|
||||
|
||||
public static Achievement From(in Guid archiveId, in AchievementId id)
|
||||
public static Achievement From(Guid archiveId, AchievementId id)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -44,11 +46,11 @@ internal sealed class Achievement : IAppDbEntityHasArchive,
|
||||
};
|
||||
}
|
||||
|
||||
public static Achievement From(in Guid userId, in UIAFItem uiaf)
|
||||
public static Achievement From(Guid archiveId, UIAFItem uiaf)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
ArchiveId = userId,
|
||||
ArchiveId = archiveId,
|
||||
Id = uiaf.Id,
|
||||
Current = uiaf.Current,
|
||||
Status = uiaf.Status,
|
||||
@@ -56,6 +58,18 @@ internal sealed class Achievement : IAppDbEntityHasArchive,
|
||||
};
|
||||
}
|
||||
|
||||
public static Achievement From(Guid archiveId, HutaoReservedAchievement achievement)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
ArchiveId = archiveId,
|
||||
Id = achievement.Id,
|
||||
Current = achievement.Current,
|
||||
Status = achievement.Status,
|
||||
Time = achievement.Time,
|
||||
};
|
||||
}
|
||||
|
||||
public bool Equals(Achievement? other)
|
||||
{
|
||||
if (other is null)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Model.Entity.Abstraction;
|
||||
using Snap.Hutao.Model.InterChange;
|
||||
using Snap.Hutao.Model.InterChange.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
@@ -10,70 +11,34 @@ using System.Globalization;
|
||||
|
||||
namespace Snap.Hutao.Model.Entity;
|
||||
|
||||
/// <summary>
|
||||
/// 抽卡记录物品
|
||||
/// </summary>
|
||||
[HighQuality]
|
||||
[Table("gacha_items")]
|
||||
internal sealed partial class GachaItem
|
||||
: IDbMappingForeignKeyFrom<GachaItem, GachaLogItem, uint>,
|
||||
IDbMappingForeignKeyFrom<GachaItem, LegacyUIGFItem, uint>,
|
||||
IDbMappingForeignKeyFrom<GachaItem, LegacyUIGFItem>,
|
||||
IDbMappingForeignKeyFrom<GachaItem, Web.Hutao.GachaLog.GachaItem>
|
||||
IDbMappingForeignKeyFrom<GachaItem, Web.Hutao.GachaLog.GachaItem>,
|
||||
IDbMappingForeignKeyFrom<GachaItem, Hk4eItem, int>
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部Id
|
||||
/// </summary>
|
||||
[Key]
|
||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||
public Guid InnerId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 存档
|
||||
/// </summary>
|
||||
[ForeignKey(nameof(ArchiveId))]
|
||||
public GachaArchive Archive { get; set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// 存档Id
|
||||
/// </summary>
|
||||
public Guid ArchiveId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录分类
|
||||
/// </summary>
|
||||
public GachaType GachaType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 祈愿记录查询分类
|
||||
/// 合并保底的卡池使用此属性
|
||||
/// 仅4种(不含400)
|
||||
/// </summary>
|
||||
public GachaType QueryType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品Id
|
||||
/// </summary>
|
||||
public uint ItemId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取时间
|
||||
/// </summary>
|
||||
public DateTimeOffset Time { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 物品
|
||||
/// </summary>
|
||||
public long Id { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库祈愿物品
|
||||
/// </summary>
|
||||
/// <param name="archiveId">存档Id</param>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem From(in Guid archiveId, in GachaLogItem item, in uint itemId)
|
||||
public static GachaItem From(Guid archiveId, GachaLogItem item, uint itemId)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -86,14 +51,7 @@ internal sealed partial class GachaItem
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库祈愿物品
|
||||
/// </summary>
|
||||
/// <param name="archiveId">存档Id</param>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <param name="itemId">物品Id</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem From(in Guid archiveId, in LegacyUIGFItem item, in uint itemId)
|
||||
public static GachaItem From(Guid archiveId, LegacyUIGFItem item, uint itemId)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -106,13 +64,7 @@ internal sealed partial class GachaItem
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库祈愿物品
|
||||
/// </summary>
|
||||
/// <param name="archiveId">存档Id</param>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem From(in Guid archiveId, in LegacyUIGFItem item)
|
||||
public static GachaItem From(Guid archiveId, LegacyUIGFItem item)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -125,13 +77,7 @@ internal sealed partial class GachaItem
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造一个新的数据库祈愿物品
|
||||
/// </summary>
|
||||
/// <param name="archiveId">存档Id</param>
|
||||
/// <param name="item">祈愿物品</param>
|
||||
/// <returns>新的祈愿物品</returns>
|
||||
public static GachaItem From(in Guid archiveId, in Web.Hutao.GachaLog.GachaItem item)
|
||||
public static GachaItem From(Guid archiveId, Web.Hutao.GachaLog.GachaItem item)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
@@ -143,4 +89,17 @@ internal sealed partial class GachaItem
|
||||
Id = item.Id,
|
||||
};
|
||||
}
|
||||
|
||||
public static GachaItem From(Guid archiveId, Hk4eItem item, int timezone)
|
||||
{
|
||||
return new()
|
||||
{
|
||||
ArchiveId = archiveId,
|
||||
GachaType = item.GachaType,
|
||||
QueryType = item.UIGFGachaType,
|
||||
ItemId = item.ItemId,
|
||||
Time = new(item.Time, new(timezone, 0, 0)),
|
||||
Id = item.Id,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -83,4 +83,14 @@ internal sealed partial class AchievementDbService : IAchievementDbService
|
||||
{
|
||||
return this.SingleOrDefault<AchievementArchive>(a => a.InnerId == archiveId);
|
||||
}
|
||||
|
||||
public AchievementArchive? GetAchievementArchiveByName(string name)
|
||||
{
|
||||
return this.SingleOrDefault<AchievementArchive>(a => a.Name == name);
|
||||
}
|
||||
|
||||
public void AddAchievementArchive(AchievementArchive archive)
|
||||
{
|
||||
this.Add(archive);
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,16 @@ namespace Snap.Hutao.Service.Achievement;
|
||||
|
||||
internal interface IAchievementDbService : IAppDbService<EntityArchive>, IAppDbService<EntityAchievement>
|
||||
{
|
||||
void AddAchievementArchive(EntityArchive archive);
|
||||
|
||||
ObservableCollection<EntityArchive> GetAchievementArchiveCollection();
|
||||
|
||||
List<EntityArchive> GetAchievementArchiveList();
|
||||
|
||||
EntityArchive? GetAchievementArchiveById(Guid archiveId);
|
||||
|
||||
EntityArchive? GetAchievementArchiveByName(string name);
|
||||
|
||||
void RemoveAchievementArchive(EntityArchive archive);
|
||||
|
||||
List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId);
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.UIGF;
|
||||
|
||||
internal interface IUIGFImportService
|
||||
{
|
||||
ValueTask<bool> ImportAsync(UIGFImportOptions importOptions, CancellationToken token);
|
||||
}
|
||||
@@ -218,20 +218,3 @@ internal sealed partial class UIGF40ExportService : IUIGFExportService
|
||||
hutaoReserved.SpiralAbyss = results;
|
||||
}
|
||||
}
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Transient, typeof(IUIGFImportService), Key = UIGFVersion.UIGF40)]
|
||||
internal sealed partial class UIGF40ImportService : IUIGFImportService
|
||||
{
|
||||
public async ValueTask<bool> ImportAsync(UIGFImportOptions importOptions, CancellationToken token)
|
||||
{
|
||||
await Task.CompletedTask;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal interface IUIGFImportService
|
||||
{
|
||||
|
||||
}
|
||||
105
src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ImportService.cs
Normal file
105
src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGF40ImportService.cs
Normal file
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using Snap.Hutao.Core;
|
||||
using Snap.Hutao.Model.Entity;
|
||||
using Snap.Hutao.Model.InterChange;
|
||||
using Snap.Hutao.Service.Achievement;
|
||||
using Snap.Hutao.Service.GachaLog;
|
||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||
|
||||
namespace Snap.Hutao.Service.UIGF;
|
||||
|
||||
[ConstructorGenerated]
|
||||
[Injection(InjectAs.Transient, typeof(IUIGFImportService), Key = UIGFVersion.UIGF40)]
|
||||
internal sealed partial class UIGF40ImportService : IUIGFImportService
|
||||
{
|
||||
private readonly JsonSerializerOptions jsonOptions;
|
||||
private readonly IServiceProvider serviceProvider;
|
||||
private readonly RuntimeOptions runtimeOptions;
|
||||
private readonly ITaskContext taskContext;
|
||||
|
||||
public async ValueTask<bool> ImportAsync(UIGFImportOptions importOptions, CancellationToken token)
|
||||
{
|
||||
await taskContext.SwitchToBackgroundAsync();
|
||||
ImportGachaArchives(importOptions.UIGF.Hk4e, importOptions.GachaArchiveUids);
|
||||
ImportAchievementArchives(importOptions.UIGF.HutaoReserved?.Achievement, importOptions.ReservedAchievementArchiveIdentities);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void ImportGachaArchives(List<UIGFEntry<Hk4eItem>>? entries, HashSet<string> uids)
|
||||
{
|
||||
if (entries.IsNullOrEmpty() || uids.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IGachaLogDbService gachaLogDbService = serviceProvider.GetRequiredService<IGachaLogDbService>();
|
||||
|
||||
foreach (UIGFEntry<Hk4eItem> entry in entries)
|
||||
{
|
||||
if (!uids.Contains(entry.Uid))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
GachaArchive? archive = gachaLogDbService.GetGachaArchiveByUid(entry.Uid);
|
||||
|
||||
if (archive is null)
|
||||
{
|
||||
archive = GachaArchive.From(entry.Uid);
|
||||
gachaLogDbService.AddGachaArchive(archive);
|
||||
}
|
||||
|
||||
Guid archiveId = archive.InnerId;
|
||||
|
||||
List<GachaItem> fullItems = [];
|
||||
foreach (GachaType queryType in GachaLog.GachaLog.QueryTypes)
|
||||
{
|
||||
long trimId = gachaLogDbService.GetOldestGachaItemIdByArchiveIdAndQueryType(archiveId, queryType);
|
||||
List<GachaItem> currentTypedList = entry.List
|
||||
.Where(item => item.UIGFGachaType == queryType && item.Id > trimId)
|
||||
.OrderByDescending(item => item.Id)
|
||||
.Select(item => GachaItem.From(archiveId, item, entry.TimeZone))
|
||||
.ToList();
|
||||
|
||||
fullItems.AddRange(currentTypedList);
|
||||
}
|
||||
|
||||
gachaLogDbService.AddGachaItemRange(fullItems);
|
||||
}
|
||||
}
|
||||
|
||||
private void ImportAchievementArchives(List<HutaoReservedEntry<HutaoReservedAchievement>>? entries, HashSet<string> identities)
|
||||
{
|
||||
if (entries.IsNullOrEmpty() || identities.IsNullOrEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IAchievementDbService achievementDbService = serviceProvider.GetRequiredService<IAchievementDbService>();
|
||||
AchievementDbBulkOperation achievementDbBulkOperation = serviceProvider.GetRequiredService<AchievementDbBulkOperation>();
|
||||
|
||||
foreach (HutaoReservedEntry<HutaoReservedAchievement> entry in entries)
|
||||
{
|
||||
if (!identities.Contains(entry.Identity))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AchievementArchive? archive = achievementDbService.GetAchievementArchiveByName(entry.Identity);
|
||||
|
||||
if (archive is null)
|
||||
{
|
||||
archive = AchievementArchive.From(entry.Identity);
|
||||
achievementDbService.AddAchievementArchive(archive);
|
||||
}
|
||||
|
||||
Guid archiveId = archive.InnerId;
|
||||
|
||||
List<Model.Entity.Achievement> achievements = entry.List.SelectList(item => Model.Entity.Achievement.From(archiveId, item));
|
||||
achievementDbBulkOperation.Overwrite(archiveId, achievements);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,12 +17,3 @@ internal sealed class UIGFExportOptions
|
||||
|
||||
public required List<string> ReservedSpiralAbyssUids { get; set; }
|
||||
}
|
||||
|
||||
internal sealed class UIGFImportOptions
|
||||
{
|
||||
public Model.InterChange.UIGF UIGF { get; set; } = default!;
|
||||
|
||||
public HashSet<string> GachaArchiveUids { get; set; } = default!;
|
||||
|
||||
public HashSet<string> ReservedAchievementArchiveIdentities { get; set; } = default!;
|
||||
}
|
||||
19
src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFImportOptions.cs
Normal file
19
src/Snap.Hutao/Snap.Hutao/Service/UIGF/UIGFImportOptions.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) DGP Studio. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
namespace Snap.Hutao.Service.UIGF;
|
||||
|
||||
internal sealed class UIGFImportOptions
|
||||
{
|
||||
public required Model.InterChange.UIGF UIGF { get; set; }
|
||||
|
||||
public required HashSet<string> GachaArchiveUids { get; set; }
|
||||
|
||||
public required HashSet<string> ReservedAchievementArchiveIdentities { get; set; }
|
||||
|
||||
public required HashSet<string> ReservedAvatarInfoIdentities { get; set; }
|
||||
|
||||
public required HashSet<string> ReservedCultivationProjectIdentities { get; set; }
|
||||
|
||||
public required HashSet<string> ReservedSpiralAbyssIdentities { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user