mirror of
https://jihulab.com/DGP-Studio/Snap.Hutao.git
synced 2025-11-19 21:02:53 +08:00
Compare commits
1 Commits
feat/unhel
...
revert/qr_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a845dff6ee |
@@ -10,6 +10,8 @@ using Snap.Hutao.Core.LifeCycle.InterProcess;
|
|||||||
using Snap.Hutao.Core.Logging;
|
using Snap.Hutao.Core.Logging;
|
||||||
using Snap.Hutao.Core.Shell;
|
using Snap.Hutao.Core.Shell;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Text;
|
||||||
|
using static Snap.Hutao.Core.Logging.ConsoleVirtualTerminalSequences;
|
||||||
|
|
||||||
namespace Snap.Hutao;
|
namespace Snap.Hutao;
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
|
using Microsoft.UI.Xaml.Controls;
|
||||||
|
using Microsoft.UI.Xaml.Controls.Primitives;
|
||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
|
|
||||||
namespace Snap.Hutao.Control.Panel;
|
namespace Snap.Hutao.Control.Panel;
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Core.Collection;
|
|
||||||
|
|
||||||
internal sealed class TwoEnumerbleEnumerator<TFirst, TSecond> : IDisposable
|
|
||||||
{
|
|
||||||
private readonly IEnumerator<TFirst> firstEnumerator;
|
|
||||||
private readonly IEnumerator<TSecond> secondEnumerator;
|
|
||||||
|
|
||||||
public TwoEnumerbleEnumerator(IEnumerable<TFirst> firstEnumerable, IEnumerable<TSecond> secondEnumerable)
|
|
||||||
{
|
|
||||||
firstEnumerator = firstEnumerable.GetEnumerator();
|
|
||||||
secondEnumerator = secondEnumerable.GetEnumerator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public (TFirst First, TSecond Second) Current { get => (firstEnumerator.Current, secondEnumerator.Current); }
|
|
||||||
|
|
||||||
public bool MoveNext(ref bool moveFirst, ref bool moveSecond)
|
|
||||||
{
|
|
||||||
moveFirst = moveFirst && firstEnumerator.MoveNext();
|
|
||||||
moveSecond = moveSecond && secondEnumerator.MoveNext();
|
|
||||||
|
|
||||||
return moveFirst || moveSecond;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
firstEnumerator.Dispose();
|
|
||||||
secondEnumerator.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -13,6 +13,13 @@ namespace Snap.Hutao.Core.Database;
|
|||||||
[HighQuality]
|
[HighQuality]
|
||||||
internal static class DbSetExtension
|
internal static class DbSetExtension
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 添加并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
public static int AddAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
public static int AddAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
@@ -20,13 +27,27 @@ internal static class DbSetExtension
|
|||||||
return dbSet.SaveChangesAndClearChangeTracker();
|
return dbSet.SaveChangesAndClearChangeTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueTask<int> AddAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity, CancellationToken token = default)
|
/// <summary>
|
||||||
|
/// 异步添加并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
|
public static ValueTask<int> AddAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
dbSet.Add(entity);
|
dbSet.Add(entity);
|
||||||
return dbSet.SaveChangesAndClearChangeTrackerAsync(token);
|
return dbSet.SaveChangesAndClearChangeTrackerAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加列表并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entities">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
public static int AddRangeAndSave<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
public static int AddRangeAndSave<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
@@ -34,13 +55,27 @@ internal static class DbSetExtension
|
|||||||
return dbSet.SaveChangesAndClearChangeTracker();
|
return dbSet.SaveChangesAndClearChangeTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueTask<int> AddRangeAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities, CancellationToken token = default)
|
/// <summary>
|
||||||
|
/// 异步添加列表并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entities">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
|
public static ValueTask<int> AddRangeAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, IEnumerable<TEntity> entities)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
dbSet.AddRange(entities);
|
dbSet.AddRange(entities);
|
||||||
return dbSet.SaveChangesAndClearChangeTrackerAsync(token);
|
return dbSet.SaveChangesAndClearChangeTrackerAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
public static int RemoveAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
public static int RemoveAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
@@ -48,13 +83,27 @@ internal static class DbSetExtension
|
|||||||
return dbSet.SaveChangesAndClearChangeTracker();
|
return dbSet.SaveChangesAndClearChangeTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueTask<int> RemoveAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity, CancellationToken token = default)
|
/// <summary>
|
||||||
|
/// 异步移除并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
|
public static ValueTask<int> RemoveAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
dbSet.Remove(entity);
|
dbSet.Remove(entity);
|
||||||
return dbSet.SaveChangesAndClearChangeTrackerAsync(token);
|
return dbSet.SaveChangesAndClearChangeTrackerAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
public static int UpdateAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
public static int UpdateAndSave<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
@@ -62,11 +111,18 @@ internal static class DbSetExtension
|
|||||||
return dbSet.SaveChangesAndClearChangeTracker();
|
return dbSet.SaveChangesAndClearChangeTracker();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueTask<int> UpdateAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity, CancellationToken token = default)
|
/// <summary>
|
||||||
|
/// 异步更新并保存
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEntity">实体类型</typeparam>
|
||||||
|
/// <param name="dbSet">数据库集</param>
|
||||||
|
/// <param name="entity">实体</param>
|
||||||
|
/// <returns>影响条数</returns>
|
||||||
|
public static ValueTask<int> UpdateAndSaveAsync<TEntity>(this DbSet<TEntity> dbSet, TEntity entity)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
dbSet.Update(entity);
|
dbSet.Update(entity);
|
||||||
return dbSet.SaveChangesAndClearChangeTrackerAsync(token);
|
return dbSet.SaveChangesAndClearChangeTrackerAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -80,11 +136,11 @@ internal static class DbSetExtension
|
|||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
private static async ValueTask<int> SaveChangesAndClearChangeTrackerAsync<TEntity>(this DbSet<TEntity> dbSet, CancellationToken token = default)
|
private static async ValueTask<int> SaveChangesAndClearChangeTrackerAsync<TEntity>(this DbSet<TEntity> dbSet)
|
||||||
where TEntity : class
|
where TEntity : class
|
||||||
{
|
{
|
||||||
DbContext dbContext = dbSet.Context();
|
DbContext dbContext = dbSet.Context();
|
||||||
int count = await dbContext.SaveChangesAsync(token).ConfigureAwait(false);
|
int count = await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
dbContext.ChangeTracker.Clear();
|
dbContext.ChangeTracker.Clear();
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,12 +47,6 @@ internal sealed class HutaoException : Exception
|
|||||||
throw new HutaoException(HutaoExceptionKind.GachaStatisticsInvalidItemId, message, innerException);
|
throw new HutaoException(HutaoExceptionKind.GachaStatisticsInvalidItemId, message, innerException);
|
||||||
}
|
}
|
||||||
|
|
||||||
[DoesNotReturn]
|
|
||||||
public static HutaoException UserdataCorrupted(string message, Exception? innerException = default)
|
|
||||||
{
|
|
||||||
throw new HutaoException(HutaoExceptionKind.UserdataCorrupted, message, innerException);
|
|
||||||
}
|
|
||||||
|
|
||||||
[DoesNotReturn]
|
[DoesNotReturn]
|
||||||
public static InvalidCastException InvalidCast<TFrom, TTo>(string name, Exception? innerException = default)
|
public static InvalidCastException InvalidCast<TFrom, TTo>(string name, Exception? innerException = default)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ internal enum HutaoExceptionKind
|
|||||||
|
|
||||||
// Foundation
|
// Foundation
|
||||||
ImageCacheInvalidUri,
|
ImageCacheInvalidUri,
|
||||||
DatabaseCorrupted,
|
|
||||||
UserdataCorrupted,
|
|
||||||
|
|
||||||
// IO
|
// IO
|
||||||
FileSystemCreateFileInsufficientPermissions,
|
FileSystemCreateFileInsufficientPermissions,
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Model.Entity.Abstraction;
|
|
||||||
|
|
||||||
internal interface IAppDbEntity
|
|
||||||
{
|
|
||||||
Guid InnerId { get; set; }
|
|
||||||
}
|
|
||||||
@@ -16,8 +16,8 @@ namespace Snap.Hutao.Model.Entity;
|
|||||||
[HighQuality]
|
[HighQuality]
|
||||||
[Table("achievements")]
|
[Table("achievements")]
|
||||||
[SuppressMessage("", "SA1124")]
|
[SuppressMessage("", "SA1124")]
|
||||||
internal sealed class Achievement : IAppDbEntity,
|
internal sealed class Achievement
|
||||||
IEquatable<Achievement>,
|
: IEquatable<Achievement>,
|
||||||
IDbMappingForeignKeyFrom<Achievement, AchievementId>,
|
IDbMappingForeignKeyFrom<Achievement, AchievementId>,
|
||||||
IDbMappingForeignKeyFrom<Achievement, UIAFItem>
|
IDbMappingForeignKeyFrom<Achievement, UIAFItem>
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ internal sealed partial class SettingEntry
|
|||||||
public const string AnnouncementRegion = "AnnouncementRegion";
|
public const string AnnouncementRegion = "AnnouncementRegion";
|
||||||
|
|
||||||
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
|
public const string IsEmptyHistoryWishVisible = "IsEmptyHistoryWishVisible";
|
||||||
public const string IsNeverHeldStatisticsItemVisible = "IsNeverHeldStatisticsItemVisible";
|
|
||||||
|
|
||||||
public const string GeetestCustomCompositeUrl = "GeetestCustomCompositeUrl";
|
public const string GeetestCustomCompositeUrl = "GeetestCustomCompositeUrl";
|
||||||
|
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>Open UIAF Json File</value>
|
<value>Open UIAF Json File</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>Multiple identical achievement IDs found in a single achievement archive</value>
|
<value>Multiple identical achievement IDs found in a single achievement archive</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>Buka berkas UIAF Json</value>
|
<value>Buka berkas UIAF Json</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>Terdapat beberapa ID pencapaian yang identik dalam satu arsip pencapaian</value>
|
<value>Terdapat beberapa ID pencapaian yang identik dalam satu arsip pencapaian</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>UIAF Json ファイルを開く</value>
|
<value>UIAF Json ファイルを開く</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>複数の同一アチーブメント Idがアーカイブに混在しています</value>
|
<value>複数の同一アチーブメント Idがアーカイブに混在しています</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>打开 UIAF Json 文件</value>
|
<value>打开 UIAF Json 文件</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>한 업적 아카이브에서 Id 동일한 업적 발견됨</value>
|
<value>한 업적 아카이브에서 Id 동일한 업적 발견됨</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>Abrir arquivo Json UIAF</value>
|
<value>Abrir arquivo Json UIAF</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>Várias IDs de conquistas idênticas encontradas em um único arquivo de conquistas</value>
|
<value>Várias IDs de conquistas idênticas encontradas em um único arquivo de conquistas</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>打开 UIAF Json 文件</value>
|
<value>打开 UIAF Json 文件</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>单个成就存档内发现多个相同的成就 Id</value>
|
<value>单个成就存档内发现多个相同的成就 Id</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
@@ -2636,12 +2636,6 @@
|
|||||||
<data name="ViewPageSettingKeyShortcutHeader" xml:space="preserve">
|
<data name="ViewPageSettingKeyShortcutHeader" xml:space="preserve">
|
||||||
<value>快捷键</value>
|
<value>快捷键</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ViewPageSettingNeverHeldItemVisibleDescription" xml:space="preserve">
|
|
||||||
<value>在祈愿记录页面显示或隐藏未持有过的角色或武器</value>
|
|
||||||
</data>
|
|
||||||
<data name="ViewPageSettingNeverHeldItemVisibleHeader" xml:space="preserve">
|
|
||||||
<value>未持有过的角色或武器</value>
|
|
||||||
</data>
|
|
||||||
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
<data name="ViewPageSettingOfficialSiteNavigate" xml:space="preserve">
|
||||||
<value>前往官网</value>
|
<value>前往官网</value>
|
||||||
</data>
|
</data>
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>Открыть UIAF Json файл</value>
|
<value>Открыть UIAF Json файл</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>В одном архиве достижений обнаружено несколько одинаковых идентификаторов достижений</value>
|
<value>В одном архиве достижений обнаружено несколько одинаковых идентификаторов достижений</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -60,45 +60,45 @@
|
|||||||
: and then encoded with base64 encoding.
|
: and then encoded with base64 encoding.
|
||||||
-->
|
-->
|
||||||
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
<xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root">
|
||||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
<xsd:import namespace="http://www.w3.org/XML/1998/namespace"/>
|
||||||
<xsd:element name="root" msdata:IsDataSet="true">
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:choice maxOccurs="unbounded">
|
<xsd:choice maxOccurs="unbounded">
|
||||||
<xsd:element name="metadata">
|
<xsd:element name="metadata">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
<xsd:attribute name="name" use="required" type="xsd:string"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" />
|
<xsd:attribute name="type" type="xsd:string"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
<xsd:attribute name="mimetype" type="xsd:string"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="assembly">
|
<xsd:element name="assembly">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:attribute name="alias" type="xsd:string" />
|
<xsd:attribute name="alias" type="xsd:string"/>
|
||||||
<xsd:attribute name="name" type="xsd:string" />
|
<xsd:attribute name="name" type="xsd:string"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="data">
|
<xsd:element name="data">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1"/>
|
||||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3"/>
|
||||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4"/>
|
||||||
<xsd:attribute ref="xml:space" />
|
<xsd:attribute ref="xml:space"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
<xsd:element name="resheader">
|
<xsd:element name="resheader">
|
||||||
<xsd:complexType>
|
<xsd:complexType>
|
||||||
<xsd:sequence>
|
<xsd:sequence>
|
||||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1"/>
|
||||||
</xsd:sequence>
|
</xsd:sequence>
|
||||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
<xsd:attribute name="name" type="xsd:string" use="required"/>
|
||||||
</xsd:complexType>
|
</xsd:complexType>
|
||||||
</xsd:element>
|
</xsd:element>
|
||||||
</xsd:choice>
|
</xsd:choice>
|
||||||
@@ -653,7 +653,7 @@
|
|||||||
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
<data name="ServiceAchievementUIAFImportPickerTitile" xml:space="preserve">
|
||||||
<value>打開 UIAF Json 文件</value>
|
<value>打開 UIAF Json 文件</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAchievementUserdataCorruptedAchievementIdNotUnique" xml:space="preserve">
|
<data name="ServiceAchievementUserdataCorruptedInnerIdNotUnique" xml:space="preserve">
|
||||||
<value>單個成就存檔內發現多個相同的成就 Id</value>
|
<value>單個成就存檔內發現多個相同的成就 Id</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
<data name="ServiceAvatarInfoPropertyAtk" xml:space="preserve">
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Snap.Hutao.Core.Database;
|
|
||||||
using Snap.Hutao.Model.Entity.Abstraction;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Abstraction;
|
|
||||||
|
|
||||||
internal static class AppDbServiceAppDbEntityExtension
|
|
||||||
{
|
|
||||||
public static int DeleteByInnerId<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
|
|
||||||
where TEntity : class, IAppDbEntity
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => dbset.ExecuteDeleteWhere(e => e.InnerId == entity.InnerId));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<int> DeleteByInnerIdAsync<TEntity>(this IAppDbService<TEntity> service, TEntity entity, CancellationToken token = default)
|
|
||||||
where TEntity : class, IAppDbEntity
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => dbset.ExecuteDeleteWhereAsync(e => e.InnerId == entity.InnerId, token), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,152 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Snap.Hutao.Core.Database;
|
|
||||||
using Snap.Hutao.Model.Entity.Database;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Abstraction;
|
|
||||||
|
|
||||||
internal static class AppDbServiceExtension
|
|
||||||
{
|
|
||||||
public static TResult Execute<TEntity, TResult>(this IAppDbService<TEntity> service, Func<DbSet<TEntity>, TResult> func)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
using (IServiceScope scope = service.ServiceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
|
||||||
return func(appDbContext.Set<TEntity>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async ValueTask<TResult> ExecuteAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<DbSet<TEntity>, ValueTask<TResult>> asyncFunc)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
using (IServiceScope scope = service.ServiceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
|
||||||
return await asyncFunc(appDbContext.Set<TEntity>()).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async ValueTask<TResult> ExecuteAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<DbSet<TEntity>, CancellationToken, ValueTask<TResult>> asyncFunc, CancellationToken token)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
using (IServiceScope scope = service.ServiceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
|
||||||
return await asyncFunc(appDbContext.Set<TEntity>(), token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async ValueTask<TResult> ExecuteAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<DbSet<TEntity>, Task<TResult>> asyncFunc)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
using (IServiceScope scope = service.ServiceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
|
||||||
return await asyncFunc(appDbContext.Set<TEntity>()).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static async ValueTask<TResult> ExecuteAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<DbSet<TEntity>, CancellationToken, Task<TResult>> asyncFunc, CancellationToken token)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
using (IServiceScope scope = service.ServiceProvider.CreateScope())
|
|
||||||
{
|
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
|
||||||
return await asyncFunc(appDbContext.Set<TEntity>(), token).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int Add<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => dbset.AddAndSave(entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<int> AddAsync<TEntity>(this IAppDbService<TEntity> service, TEntity entity, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => dbset.AddAndSaveAsync(entity, token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int AddRange<TEntity>(this IAppDbService<TEntity> service, IEnumerable<TEntity> entities)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => dbset.AddRangeAndSave(entities));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<int> AddRangeAsync<TEntity>(this IAppDbService<TEntity> service, IEnumerable<TEntity> entities, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => dbset.AddRangeAndSaveAsync(entities, token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TEntity Single<TEntity>(this IAppDbService<TEntity> service, Expression<Func<TEntity, bool>> predicate)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => dbset.AsNoTracking().Single(predicate));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<TEntity> SingleAsync<TEntity>(this IAppDbService<TEntity> service, Expression<Func<TEntity, bool>> predicate, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => dbset.AsNoTracking().SingleAsync(predicate, token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TResult Query<TEntity, TResult>(this IAppDbService<TEntity> service, Func<IQueryable<TEntity>, TResult> func)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => func(dbset.AsNoTracking()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<TResult> QueryAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<IQueryable<TEntity>, ValueTask<TResult>> func)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync(dbset => func(dbset.AsNoTracking()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<TResult> QueryAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<IQueryable<TEntity>, CancellationToken, ValueTask<TResult>> func, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => func(dbset.AsNoTracking(), token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<TResult> QueryAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<IQueryable<TEntity>, Task<TResult>> func)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync(dbset => func(dbset.AsNoTracking()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<TResult> QueryAsync<TEntity, TResult>(this IAppDbService<TEntity> service, Func<IQueryable<TEntity>, CancellationToken, Task<TResult>> func, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => func(dbset.AsNoTracking(), token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int Update<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => dbset.UpdateAndSave(entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<int> UpdateAsync<TEntity>(this IAppDbService<TEntity> service, TEntity entity, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => dbset.UpdateAndSaveAsync(entity, token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int Delete<TEntity>(this IAppDbService<TEntity> service, TEntity entity)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.Execute(dbset => dbset.RemoveAndSave(entity));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ValueTask<int> DeleteAsync<TEntity>(this IAppDbService<TEntity> service, TEntity entity, CancellationToken token = default)
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
return service.ExecuteAsync((dbset, token) => dbset.RemoveAndSaveAsync(entity, token), token);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -14,10 +14,20 @@ namespace Snap.Hutao.Service.Abstraction;
|
|||||||
/// 数据库存储选项的设置
|
/// 数据库存储选项的设置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ConstructorGenerated]
|
[ConstructorGenerated]
|
||||||
internal abstract partial class DbStoreOptions : ObservableObject
|
internal abstract partial class DbStoreOptions : ObservableObject, IOptions<DbStoreOptions>
|
||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public DbStoreOptions Value { get => this; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从数据库中获取字符串数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>值</returns>
|
||||||
protected string GetOption(ref string? storage, string key, string defaultValue = "")
|
protected string GetOption(ref string? storage, string key, string defaultValue = "")
|
||||||
{
|
{
|
||||||
return GetOption(ref storage, key, () => defaultValue);
|
return GetOption(ref storage, key, () => defaultValue);
|
||||||
@@ -39,6 +49,13 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从数据库中获取bool数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>值</returns>
|
||||||
protected bool GetOption(ref bool? storage, string key, bool defaultValue = false)
|
protected bool GetOption(ref bool? storage, string key, bool defaultValue = false)
|
||||||
{
|
{
|
||||||
return GetOption(ref storage, key, () => defaultValue);
|
return GetOption(ref storage, key, () => defaultValue);
|
||||||
@@ -61,6 +78,13 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
return storage.Value;
|
return storage.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从数据库中获取int数据
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>值</returns>
|
||||||
protected int GetOption(ref int? storage, string key, int defaultValue = 0)
|
protected int GetOption(ref int? storage, string key, int defaultValue = 0)
|
||||||
{
|
{
|
||||||
return GetOption(ref storage, key, () => defaultValue);
|
return GetOption(ref storage, key, () => defaultValue);
|
||||||
@@ -83,6 +107,15 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
return storage.Value;
|
return storage.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 从数据库中获取任何类型的数据
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">数据的类型</typeparam>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="deserializer">反序列化器</param>
|
||||||
|
/// <param name="defaultValue">默认值</param>
|
||||||
|
/// <returns>值</returns>
|
||||||
[return: NotNull]
|
[return: NotNull]
|
||||||
protected T GetOption<T>(ref T? storage, string key, Func<string, T> deserializer, [DisallowNull] T defaultValue)
|
protected T GetOption<T>(ref T? storage, string key, Func<string, T> deserializer, [DisallowNull] T defaultValue)
|
||||||
{
|
{
|
||||||
@@ -127,6 +160,13 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
return storage;
|
return storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将值存入数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
/// <param name="propertyName">属性名称</param>
|
||||||
protected void SetOption(ref string? storage, string key, string? value, [CallerMemberName] string? propertyName = null)
|
protected void SetOption(ref string? storage, string key, string? value, [CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
if (!SetProperty(ref storage, value, propertyName))
|
if (!SetProperty(ref storage, value, propertyName))
|
||||||
@@ -142,6 +182,14 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将值存入数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
/// <param name="propertyName">属性名称</param>
|
||||||
|
/// <returns>是否设置了值</returns>
|
||||||
protected bool SetOption(ref bool? storage, string key, bool value, [CallerMemberName] string? propertyName = null)
|
protected bool SetOption(ref bool? storage, string key, bool value, [CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
bool set = SetProperty(ref storage, value, propertyName);
|
bool set = SetProperty(ref storage, value, propertyName);
|
||||||
@@ -160,6 +208,13 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将值存入数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
/// <param name="propertyName">属性名称</param>
|
||||||
protected void SetOption(ref int? storage, string key, int value, [CallerMemberName] string? propertyName = null)
|
protected void SetOption(ref int? storage, string key, int value, [CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
if (!SetProperty(ref storage, value, propertyName))
|
if (!SetProperty(ref storage, value, propertyName))
|
||||||
@@ -175,6 +230,15 @@ internal abstract partial class DbStoreOptions : ObservableObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将值存入数据库
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">数据的类型</typeparam>
|
||||||
|
/// <param name="storage">存储字段</param>
|
||||||
|
/// <param name="key">键</param>
|
||||||
|
/// <param name="value">值</param>
|
||||||
|
/// <param name="serializer">序列化器</param>
|
||||||
|
/// <param name="propertyName">属性名称</param>
|
||||||
protected void SetOption<T>(ref T? storage, string key, T value, Func<T, string> serializer, [CallerMemberName] string? propertyName = null)
|
protected void SetOption<T>(ref T? storage, string key, T value, Func<T, string> serializer, [CallerMemberName] string? propertyName = null)
|
||||||
{
|
{
|
||||||
if (!SetProperty(ref storage, value, propertyName))
|
if (!SetProperty(ref storage, value, propertyName))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Announcement;
|
namespace Snap.Hutao.Service.Abstraction;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 公告服务
|
/// 公告服务
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Abstraction;
|
|
||||||
|
|
||||||
internal interface IAppDbService<TEntity> : IAppInfrastructureService
|
|
||||||
where TEntity : class
|
|
||||||
{
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Abstraction;
|
|
||||||
|
|
||||||
internal interface IAppInfrastructureService
|
|
||||||
{
|
|
||||||
IServiceProvider ServiceProvider { get; }
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Abstraction;
|
|
||||||
|
|
||||||
internal interface IAppService;
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
|
||||||
// Licensed under the MIT license.
|
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Snap.Hutao.Model.Entity.Database;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Abstraction;
|
|
||||||
|
|
||||||
internal static class ServiceScopeExtension
|
|
||||||
{
|
|
||||||
public static TService GetRequiredService<TService>(this IServiceScope scope)
|
|
||||||
where TService : class
|
|
||||||
{
|
|
||||||
return scope.ServiceProvider.GetRequiredService<TService>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static TDbContext GetDbContext<TDbContext>(this IServiceScope scope)
|
|
||||||
where TDbContext : DbContext
|
|
||||||
{
|
|
||||||
return scope.GetRequiredService<TDbContext>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AppDbContext GetAppDbContext(this IServiceScope scope)
|
|
||||||
{
|
|
||||||
return scope.GetDbContext<AppDbContext>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,11 +2,9 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Snap.Hutao.Core.Collection;
|
|
||||||
using Snap.Hutao.Core.Database;
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Model.Entity.Database;
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
using Snap.Hutao.Model.InterChange.Achievement;
|
using Snap.Hutao.Model.InterChange.Achievement;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
|
||||||
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Achievement;
|
namespace Snap.Hutao.Service.Achievement;
|
||||||
@@ -23,52 +21,73 @@ internal sealed partial class AchievementDbBulkOperation
|
|||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
private readonly ILogger<AchievementDbBulkOperation> logger;
|
private readonly ILogger<AchievementDbBulkOperation> logger;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 合并
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="archiveId">成就id</param>
|
||||||
|
/// <param name="items">待合并的项</param>
|
||||||
|
/// <param name="aggressive">是否贪婪</param>
|
||||||
|
/// <returns>导入结果</returns>
|
||||||
public ImportResult Merge(Guid archiveId, IEnumerable<UIAFItem> items, bool aggressive)
|
public ImportResult Merge(Guid archiveId, IEnumerable<UIAFItem> items, bool aggressive)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Perform merge operation for [Archive: {Id}], [Aggressive: {Aggressive}]", archiveId, aggressive);
|
logger.LogInformation("Perform {Method} Operation for archive: {Id}, Aggressive: {Aggressive}", nameof(Merge), archiveId, aggressive);
|
||||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
IOrderedQueryable<EntityAchievement> oldData = appDbContext.Achievements
|
IOrderedQueryable<EntityAchievement> oldData = appDbContext.Achievements
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(a => a.ArchiveId == archiveId)
|
.Where(a => a.ArchiveId == archiveId)
|
||||||
.OrderBy(a => a.Id);
|
.OrderBy(a => a.Id);
|
||||||
|
|
||||||
(int add, int update) = (0, 0);
|
int add = 0;
|
||||||
|
int update = 0;
|
||||||
|
|
||||||
using (TwoEnumerbleEnumerator<EntityAchievement, UIAFItem> enumerator = new(oldData, items))
|
using (IEnumerator<EntityAchievement> entityEnumerator = oldData.GetEnumerator())
|
||||||
{
|
{
|
||||||
(bool moveEntity, bool moveUIAF) = (true, true);
|
using (IEnumerator<UIAFItem> uiafEnumerator = items.GetEnumerator())
|
||||||
|
{
|
||||||
|
bool moveEntity = true;
|
||||||
|
bool moveUIAF = true;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!enumerator.MoveNext(ref moveEntity, ref moveUIAF))
|
bool moveEntityResult = moveEntity && entityEnumerator.MoveNext();
|
||||||
|
bool moveUIAFResult = moveUIAF && uiafEnumerator.MoveNext();
|
||||||
|
|
||||||
|
if (!(moveEntityResult || moveUIAFResult))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
(EntityAchievement? entity, UIAFItem? uiaf) = enumerator.Current;
|
{
|
||||||
|
EntityAchievement? entity = entityEnumerator.Current;
|
||||||
switch (entity, uiaf)
|
UIAFItem? uiaf = uiafEnumerator.Current;
|
||||||
|
|
||||||
|
if (entity is null && uiaf is not null)
|
||||||
{
|
{
|
||||||
case (null, not null):
|
|
||||||
appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf));
|
appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf));
|
||||||
add++;
|
add++;
|
||||||
continue;
|
continue;
|
||||||
case (not null, null):
|
}
|
||||||
continue; // Skipped
|
else if (entity is not null && uiaf is null)
|
||||||
default:
|
{
|
||||||
|
// skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(entity);
|
ArgumentNullException.ThrowIfNull(entity);
|
||||||
ArgumentNullException.ThrowIfNull(uiaf);
|
ArgumentNullException.ThrowIfNull(uiaf);
|
||||||
|
|
||||||
switch (entity.Id.CompareTo(uiaf.Id))
|
if (entity.Id < uiaf.Id)
|
||||||
{
|
{
|
||||||
case < 0:
|
moveEntity = true;
|
||||||
(moveEntity, moveUIAF) = (true, false);
|
moveUIAF = false;
|
||||||
break;
|
}
|
||||||
case 0:
|
else if (entity.Id == uiaf.Id)
|
||||||
(moveEntity, moveUIAF) = (true, true);
|
{
|
||||||
|
moveEntity = true;
|
||||||
|
moveUIAF = true;
|
||||||
|
|
||||||
if (aggressive)
|
if (aggressive)
|
||||||
{
|
{
|
||||||
@@ -76,78 +95,96 @@ internal sealed partial class AchievementDbBulkOperation
|
|||||||
appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf));
|
appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf));
|
||||||
update++;
|
update++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
else
|
||||||
case > 0:
|
{
|
||||||
(moveEntity, moveUIAF) = (false, true);
|
// entity.Id > uiaf.Id
|
||||||
|
moveEntity = false;
|
||||||
|
moveUIAF = true;
|
||||||
|
|
||||||
appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf));
|
appDbContext.Achievements.AddAndSave(EntityAchievement.From(archiveId, uiaf));
|
||||||
add++;
|
add++;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.LogInformation("Merge operation complete, [Add: {Add}], [Update: {Update}]", add, update);
|
logger.LogInformation("{Method} Operation Complete, Add: {Add}, Update: {Update}", nameof(Merge), add, update);
|
||||||
return new(add, update, 0);
|
return new(add, update, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 覆盖
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="archiveId">成就id</param>
|
||||||
|
/// <param name="items">待覆盖的项</param>
|
||||||
|
/// <returns>导入结果</returns>
|
||||||
public ImportResult Overwrite(Guid archiveId, IEnumerable<EntityAchievement> items)
|
public ImportResult Overwrite(Guid archiveId, IEnumerable<EntityAchievement> items)
|
||||||
{
|
{
|
||||||
logger.LogInformation("Perform Overwrite Operation for [Archive: {Id}]", archiveId);
|
logger.LogInformation("Perform {Method} Operation for archive: {Id}", nameof(Overwrite), archiveId);
|
||||||
using (IServiceScope scope = serviceProvider.CreateScope())
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
{
|
{
|
||||||
AppDbContext appDbContext = scope.GetAppDbContext();
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
IOrderedQueryable<EntityAchievement> oldData = appDbContext.Achievements
|
IOrderedQueryable<EntityAchievement> oldData = appDbContext.Achievements
|
||||||
.AsNoTracking()
|
.AsNoTracking()
|
||||||
.Where(a => a.ArchiveId == archiveId)
|
.Where(a => a.ArchiveId == archiveId)
|
||||||
.OrderBy(a => a.Id);
|
.OrderBy(a => a.Id);
|
||||||
|
|
||||||
(int add, int update, int remove) = (0, 0, 0);
|
int add = 0;
|
||||||
|
int update = 0;
|
||||||
|
int remove = 0;
|
||||||
|
|
||||||
using (TwoEnumerbleEnumerator<EntityAchievement, EntityAchievement> enumerator = new(oldData, items))
|
using (IEnumerator<EntityAchievement> oldDataEnumerator = oldData.GetEnumerator())
|
||||||
{
|
{
|
||||||
(bool moveOld, bool moveNew) = (true, true);
|
using (IEnumerator<EntityAchievement> newDataEnumerator = items.GetEnumerator())
|
||||||
|
{
|
||||||
|
bool moveOld = true;
|
||||||
|
bool moveNew = true;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!enumerator.MoveNext(ref moveOld, ref moveNew))
|
bool moveOldResult = moveOld && oldDataEnumerator.MoveNext();
|
||||||
{
|
bool moveNewResult = moveNew && newDataEnumerator.MoveNext();
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
(EntityAchievement? oldEntity, EntityAchievement? newEntity) = enumerator.Current;
|
if (moveOldResult || moveNewResult)
|
||||||
|
{
|
||||||
switch (oldEntity, newEntity)
|
EntityAchievement? oldEntity = oldDataEnumerator.Current;
|
||||||
|
EntityAchievement? newEntity = newDataEnumerator.Current;
|
||||||
|
|
||||||
|
if (oldEntity is null && newEntity is not null)
|
||||||
{
|
{
|
||||||
case (null, not null):
|
|
||||||
appDbContext.Achievements.AddAndSave(newEntity);
|
appDbContext.Achievements.AddAndSave(newEntity);
|
||||||
add++;
|
add++;
|
||||||
continue;
|
continue;
|
||||||
case (not null, null):
|
}
|
||||||
|
else if (oldEntity is not null && newEntity is null)
|
||||||
|
{
|
||||||
appDbContext.Achievements.RemoveAndSave(oldEntity);
|
appDbContext.Achievements.RemoveAndSave(oldEntity);
|
||||||
remove++;
|
remove++;
|
||||||
continue;
|
continue;
|
||||||
default:
|
}
|
||||||
|
|
||||||
ArgumentNullException.ThrowIfNull(oldEntity);
|
ArgumentNullException.ThrowIfNull(oldEntity);
|
||||||
ArgumentNullException.ThrowIfNull(newEntity);
|
ArgumentNullException.ThrowIfNull(newEntity);
|
||||||
|
|
||||||
switch (oldEntity.Id.CompareTo(newEntity.Id))
|
if (oldEntity.Id < newEntity.Id)
|
||||||
{
|
{
|
||||||
case < 0:
|
moveOld = true;
|
||||||
(moveOld, moveNew) = (true, false);
|
moveNew = false;
|
||||||
break;
|
appDbContext.Achievements.RemoveAndSave(oldEntity);
|
||||||
case 0:
|
remove++;
|
||||||
(moveOld, moveNew) = (true, true);
|
}
|
||||||
|
else if (oldEntity.Id == newEntity.Id)
|
||||||
|
{
|
||||||
|
moveOld = true;
|
||||||
|
moveNew = true;
|
||||||
|
|
||||||
if (oldEntity.Equals(newEntity))
|
if (oldEntity.Equals(newEntity))
|
||||||
{
|
{
|
||||||
// Skip same entry, reduce write operation.
|
// skip same entry.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -156,22 +193,25 @@ internal sealed partial class AchievementDbBulkOperation
|
|||||||
appDbContext.Achievements.AddAndSave(newEntity);
|
appDbContext.Achievements.AddAndSave(newEntity);
|
||||||
update++;
|
update++;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
else
|
||||||
case > 0:
|
{
|
||||||
(moveOld, moveNew) = (false, true);
|
// entity.Id > uiaf.Id
|
||||||
|
moveOld = false;
|
||||||
|
moveNew = true;
|
||||||
appDbContext.Achievements.AddAndSave(newEntity);
|
appDbContext.Achievements.AddAndSave(newEntity);
|
||||||
add++;
|
add++;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger.LogInformation("Overwrite Operation Complete, Add: {Add}, Update: {Update}, Remove: {Remove}", add, update, remove);
|
logger.LogInformation("{Method} Operation Complete, Add: {Add}, Update: {Update}, Remove: {Remove}", nameof(Overwrite), add, update, remove);
|
||||||
return new(add, update, remove);
|
return new(add, update, remove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,11 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Snap.Hutao.Core.Database;
|
||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Model.Entity;
|
using Snap.Hutao.Model.Entity;
|
||||||
|
using Snap.Hutao.Model.Entity.Database;
|
||||||
using Snap.Hutao.Model.Primitive;
|
using Snap.Hutao.Model.Primitive;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
||||||
|
|
||||||
@@ -20,95 +21,148 @@ internal sealed partial class AchievementDbService : IAchievementDbService
|
|||||||
{
|
{
|
||||||
private readonly IServiceProvider serviceProvider;
|
private readonly IServiceProvider serviceProvider;
|
||||||
|
|
||||||
public IServiceProvider ServiceProvider { get => serviceProvider; }
|
|
||||||
|
|
||||||
public Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId)
|
public Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId)
|
||||||
{
|
{
|
||||||
|
Dictionary<AchievementId, EntityAchievement> entities;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return this.Query<EntityAchievement, Dictionary<AchievementId, EntityAchievement>>(query => query
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
entities = appDbContext.Achievements
|
||||||
|
.AsNoTracking()
|
||||||
.Where(a => a.ArchiveId == archiveId)
|
.Where(a => a.ArchiveId == archiveId)
|
||||||
.ToDictionary(a => (AchievementId)a.Id));
|
.ToDictionary(a => (AchievementId)a.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (ArgumentException ex)
|
catch (ArgumentException ex)
|
||||||
{
|
{
|
||||||
throw HutaoException.UserdataCorrupted(SH.ServiceAchievementUserdataCorruptedAchievementIdNotUnique, ex);
|
throw ThrowHelper.DatabaseCorrupted(SH.ServiceAchievementUserdataCorruptedInnerIdNotUnique, ex);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId, CancellationToken token = default)
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async ValueTask<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId)
|
||||||
{
|
{
|
||||||
return this.QueryAsync<EntityAchievement, int>(
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
(query, token) => query
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
return await appDbContext.Achievements
|
||||||
|
.AsNoTracking()
|
||||||
.Where(a => a.ArchiveId == archiveId)
|
.Where(a => a.ArchiveId == archiveId)
|
||||||
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
||||||
.CountAsync(token),
|
.CountAsync()
|
||||||
token);
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[SuppressMessage("", "CA1305")]
|
[SuppressMessage("", "CA1305")]
|
||||||
public ValueTask<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take, CancellationToken token = default)
|
public async ValueTask<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take)
|
||||||
{
|
{
|
||||||
return this.QueryAsync<EntityAchievement, List<EntityAchievement>>(
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
(query, token) => query
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
return await appDbContext.Achievements
|
||||||
|
.AsNoTracking()
|
||||||
.Where(a => a.ArchiveId == archiveId)
|
.Where(a => a.ArchiveId == archiveId)
|
||||||
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
.Where(a => a.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
||||||
.OrderByDescending(a => a.Time.ToString())
|
.OrderByDescending(a => a.Time.ToString())
|
||||||
.Take(take)
|
.Take(take)
|
||||||
.ToListAsync(token),
|
.ToListAsync()
|
||||||
token);
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OverwriteAchievement(EntityAchievement achievement)
|
public void OverwriteAchievement(EntityAchievement achievement)
|
||||||
{
|
{
|
||||||
this.DeleteByInnerId(achievement);
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
|
// Delete exists one.
|
||||||
|
appDbContext.Achievements.ExecuteDeleteWhere(e => e.InnerId == achievement.InnerId);
|
||||||
if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
||||||
{
|
{
|
||||||
this.Add(achievement);
|
appDbContext.Achievements.AddAndSave(achievement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask OverwriteAchievementAsync(EntityAchievement achievement, CancellationToken token = default)
|
public async ValueTask OverwriteAchievementAsync(EntityAchievement achievement)
|
||||||
{
|
{
|
||||||
await this.DeleteByInnerIdAsync(achievement, token).ConfigureAwait(false);
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
|
// Delete exists one.
|
||||||
|
await appDbContext.Achievements.ExecuteDeleteWhereAsync(e => e.InnerId == achievement.InnerId).ConfigureAwait(false);
|
||||||
if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
if (achievement.Status >= Model.Intrinsic.AchievementStatus.STATUS_FINISHED)
|
||||||
{
|
{
|
||||||
await this.AddAsync(achievement, token).ConfigureAwait(false);
|
await appDbContext.Achievements.AddAndSaveAsync(achievement).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ObservableCollection<AchievementArchive> GetAchievementArchiveCollection()
|
public ObservableCollection<AchievementArchive> GetAchievementArchiveCollection()
|
||||||
{
|
{
|
||||||
return this.Query<AchievementArchive, ObservableCollection<AchievementArchive>>(query => query.ToObservableCollection());
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
return appDbContext.AchievementArchives.AsNoTracking().ToObservableCollection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask RemoveAchievementArchiveAsync(AchievementArchive archive, CancellationToken token = default)
|
public async ValueTask RemoveAchievementArchiveAsync(AchievementArchive archive)
|
||||||
{
|
{
|
||||||
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
|
||||||
// It will cascade deleted the achievements.
|
// It will cascade deleted the achievements.
|
||||||
await this.DeleteAsync(archive, token).ConfigureAwait(false);
|
await appDbContext.AchievementArchives.RemoveAndSaveAsync(archive).ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId)
|
public List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId)
|
||||||
{
|
{
|
||||||
return this.Query<EntityAchievement, List<EntityAchievement>>(query => [.. query.Where(a => a.ArchiveId == archiveId)]);
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
IQueryable<EntityAchievement> result = appDbContext.Achievements.AsNoTracking().Where(i => i.ArchiveId == archiveId);
|
||||||
|
return [.. result];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ValueTask<List<EntityAchievement>> GetAchievementListByArchiveIdAsync(Guid archiveId, CancellationToken token = default)
|
public async ValueTask<List<EntityAchievement>> GetAchievementListByArchiveIdAsync(Guid archiveId)
|
||||||
{
|
{
|
||||||
return this.QueryAsync<EntityAchievement, List<EntityAchievement>>(
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
(query, token) => query
|
{
|
||||||
.Where(a => a.ArchiveId == archiveId)
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
.ToListAsync(token),
|
return await appDbContext.Achievements
|
||||||
token);
|
.AsNoTracking()
|
||||||
|
.Where(i => i.ArchiveId == archiveId)
|
||||||
|
.ToListAsync()
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<AchievementArchive> GetAchievementArchiveList()
|
public List<AchievementArchive> GetAchievementArchiveList()
|
||||||
{
|
{
|
||||||
return this.Query<AchievementArchive, List<AchievementArchive>>(query => [.. query]);
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
IQueryable<AchievementArchive> result = appDbContext.AchievementArchives.AsNoTracking();
|
||||||
|
return [.. result];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask<List<AchievementArchive>> GetAchievementArchiveListAsync(CancellationToken token = default)
|
public async ValueTask<List<AchievementArchive>> GetAchievementArchiveListAsync()
|
||||||
{
|
{
|
||||||
return await this.QueryAsync<AchievementArchive, List<AchievementArchive>>(query => query.ToListAsync()).ConfigureAwait(false);
|
using (IServiceScope scope = serviceProvider.CreateScope())
|
||||||
|
{
|
||||||
|
AppDbContext appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||||
|
return await appDbContext.AchievementArchives.AsNoTracking().ToListAsync().ConfigureAwait(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,33 +2,32 @@
|
|||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
using Snap.Hutao.Model.Primitive;
|
using Snap.Hutao.Model.Primitive;
|
||||||
using Snap.Hutao.Service.Abstraction;
|
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
using EntityAchievement = Snap.Hutao.Model.Entity.Achievement;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Achievement;
|
namespace Snap.Hutao.Service.Achievement;
|
||||||
|
|
||||||
internal interface IAchievementDbService : IAppDbService<Model.Entity.AchievementArchive>, IAppDbService<EntityAchievement>
|
internal interface IAchievementDbService
|
||||||
{
|
{
|
||||||
ValueTask RemoveAchievementArchiveAsync(Model.Entity.AchievementArchive archive, CancellationToken token = default);
|
ValueTask RemoveAchievementArchiveAsync(Model.Entity.AchievementArchive archive);
|
||||||
|
|
||||||
ObservableCollection<Model.Entity.AchievementArchive> GetAchievementArchiveCollection();
|
ObservableCollection<Model.Entity.AchievementArchive> GetAchievementArchiveCollection();
|
||||||
|
|
||||||
List<Model.Entity.AchievementArchive> GetAchievementArchiveList();
|
List<Model.Entity.AchievementArchive> GetAchievementArchiveList();
|
||||||
|
|
||||||
ValueTask<List<Model.Entity.AchievementArchive>> GetAchievementArchiveListAsync(CancellationToken token = default);
|
ValueTask<List<Model.Entity.AchievementArchive>> GetAchievementArchiveListAsync();
|
||||||
|
|
||||||
List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId);
|
List<EntityAchievement> GetAchievementListByArchiveId(Guid archiveId);
|
||||||
|
|
||||||
ValueTask<List<EntityAchievement>> GetAchievementListByArchiveIdAsync(Guid archiveId, CancellationToken token = default);
|
ValueTask<List<EntityAchievement>> GetAchievementListByArchiveIdAsync(Guid archiveId);
|
||||||
|
|
||||||
Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId);
|
Dictionary<AchievementId, EntityAchievement> GetAchievementMapByArchiveId(Guid archiveId);
|
||||||
|
|
||||||
ValueTask<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId, CancellationToken token = default);
|
ValueTask<int> GetFinishedAchievementCountByArchiveIdAsync(Guid archiveId);
|
||||||
|
|
||||||
ValueTask<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take, CancellationToken token = default);
|
ValueTask<List<EntityAchievement>> GetLatestFinishedAchievementListByArchiveIdAsync(Guid archiveId, int take);
|
||||||
|
|
||||||
void OverwriteAchievement(EntityAchievement achievement);
|
void OverwriteAchievement(EntityAchievement achievement);
|
||||||
|
|
||||||
ValueTask OverwriteAchievementAsync(EntityAchievement achievement, CancellationToken token = default);
|
ValueTask OverwriteAchievementAsync(EntityAchievement achievement);
|
||||||
}
|
}
|
||||||
@@ -3,14 +3,13 @@
|
|||||||
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Snap.Hutao.Core;
|
using Snap.Hutao.Core;
|
||||||
using Snap.Hutao.Service.Announcement;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Web.Hoyolab;
|
using Snap.Hutao.Web.Hoyolab;
|
||||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement;
|
||||||
using Snap.Hutao.Web.Response;
|
using Snap.Hutao.Web.Response;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using WebAnnouncement = Snap.Hutao.Web.Hoyolab.Hk4e.Common.Announcement.Announcement;
|
|
||||||
|
|
||||||
namespace Snap.Hutao.Service;
|
namespace Snap.Hutao.Service;
|
||||||
|
|
||||||
@@ -73,7 +72,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
|||||||
// 将公告内容联入公告列表
|
// 将公告内容联入公告列表
|
||||||
foreach (ref readonly AnnouncementListWrapper listWrapper in CollectionsMarshal.AsSpan(announcementListWrappers))
|
foreach (ref readonly AnnouncementListWrapper listWrapper in CollectionsMarshal.AsSpan(announcementListWrappers))
|
||||||
{
|
{
|
||||||
foreach (ref readonly WebAnnouncement item in CollectionsMarshal.AsSpan(listWrapper.List))
|
foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
|
||||||
{
|
{
|
||||||
contentMap.TryGetValue(item.AnnId, out string? rawContent);
|
contentMap.TryGetValue(item.AnnId, out string? rawContent);
|
||||||
item.Content = rawContent ?? string.Empty;
|
item.Content = rawContent ?? string.Empty;
|
||||||
@@ -84,7 +83,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
|||||||
|
|
||||||
foreach (ref readonly AnnouncementListWrapper listWrapper in CollectionsMarshal.AsSpan(announcementListWrappers))
|
foreach (ref readonly AnnouncementListWrapper listWrapper in CollectionsMarshal.AsSpan(announcementListWrappers))
|
||||||
{
|
{
|
||||||
foreach (ref readonly WebAnnouncement item in CollectionsMarshal.AsSpan(listWrapper.List))
|
foreach (ref readonly Announcement item in CollectionsMarshal.AsSpan(listWrapper.List))
|
||||||
{
|
{
|
||||||
item.Subtitle = new StringBuilder(item.Subtitle)
|
item.Subtitle = new StringBuilder(item.Subtitle)
|
||||||
.Replace("\r<br>", string.Empty)
|
.Replace("\r<br>", string.Empty)
|
||||||
@@ -100,12 +99,12 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
|||||||
private static void AdjustAnnouncementTime(List<AnnouncementListWrapper> announcementListWrappers, in TimeSpan offset)
|
private static void AdjustAnnouncementTime(List<AnnouncementListWrapper> announcementListWrappers, in TimeSpan offset)
|
||||||
{
|
{
|
||||||
// 活动公告
|
// 活动公告
|
||||||
List<WebAnnouncement> activities = announcementListWrappers
|
List<Announcement> activities = announcementListWrappers
|
||||||
.Single(wrapper => wrapper.TypeId == 1)
|
.Single(wrapper => wrapper.TypeId == 1)
|
||||||
.List;
|
.List;
|
||||||
|
|
||||||
// 更新公告
|
// 更新公告
|
||||||
WebAnnouncement versionUpdate = announcementListWrappers
|
Announcement versionUpdate = announcementListWrappers
|
||||||
.Single(wrapper => wrapper.TypeId == 2)
|
.Single(wrapper => wrapper.TypeId == 2)
|
||||||
.List
|
.List
|
||||||
.Single(ann => AnnouncementRegex.VersionUpdateTitleRegex.IsMatch(ann.Title));
|
.Single(ann => AnnouncementRegex.VersionUpdateTitleRegex.IsMatch(ann.Title));
|
||||||
@@ -117,7 +116,7 @@ internal sealed partial class AnnouncementService : IAnnouncementService
|
|||||||
|
|
||||||
DateTimeOffset versionUpdateTime = UnsafeDateTimeOffset.ParseDateTime(versionMatch.Groups[1].ValueSpan, offset);
|
DateTimeOffset versionUpdateTime = UnsafeDateTimeOffset.ParseDateTime(versionMatch.Groups[1].ValueSpan, offset);
|
||||||
|
|
||||||
foreach (ref readonly WebAnnouncement announcement in CollectionsMarshal.AsSpan(activities))
|
foreach (ref readonly Announcement announcement in CollectionsMarshal.AsSpan(activities))
|
||||||
{
|
{
|
||||||
if (AnnouncementRegex.PermanentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } permanent)
|
if (AnnouncementRegex.PermanentActivityAfterUpdateTimeRegex.Match(announcement.Content) is { Success: true } permanent)
|
||||||
{
|
{
|
||||||
@@ -15,7 +15,6 @@ namespace Snap.Hutao.Service;
|
|||||||
[Injection(InjectAs.Singleton)]
|
[Injection(InjectAs.Singleton)]
|
||||||
internal sealed partial class AppOptions : DbStoreOptions
|
internal sealed partial class AppOptions : DbStoreOptions
|
||||||
{
|
{
|
||||||
private bool? isNeverHeldStatisticsItemVisible;
|
|
||||||
private bool? isEmptyHistoryWishVisible;
|
private bool? isEmptyHistoryWishVisible;
|
||||||
private BackdropType? backdropType;
|
private BackdropType? backdropType;
|
||||||
private ElementTheme? elementTheme;
|
private ElementTheme? elementTheme;
|
||||||
@@ -29,12 +28,6 @@ internal sealed partial class AppOptions : DbStoreOptions
|
|||||||
set => SetOption(ref isEmptyHistoryWishVisible, SettingEntry.IsEmptyHistoryWishVisible, value);
|
set => SetOption(ref isEmptyHistoryWishVisible, SettingEntry.IsEmptyHistoryWishVisible, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsNeverHeldStatisticsItemVisible
|
|
||||||
{
|
|
||||||
get => GetOption(ref isNeverHeldStatisticsItemVisible, SettingEntry.IsNeverHeldStatisticsItemVisible);
|
|
||||||
set => SetOption(ref isNeverHeldStatisticsItemVisible, SettingEntry.IsNeverHeldStatisticsItemVisible, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<NameValue<BackdropType>> BackdropTypes { get; } = CollectionsNameValue.FromEnum<BackdropType>(type => type >= 0);
|
public List<NameValue<BackdropType>> BackdropTypes { get; } = CollectionsNameValue.FromEnum<BackdropType>(type => type >= 0);
|
||||||
|
|
||||||
public BackdropType BackdropType
|
public BackdropType BackdropType
|
||||||
|
|||||||
@@ -4,7 +4,6 @@
|
|||||||
using Snap.Discord.GameSDK.ABI;
|
using Snap.Discord.GameSDK.ABI;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
|
||||||
using System.Text.Unicode;
|
using System.Text.Unicode;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Discord;
|
namespace Snap.Hutao.Service.Discord;
|
||||||
@@ -159,7 +158,7 @@ internal static class DiscordController
|
|||||||
static unsafe void DebugWriteDiscordMessage(void* state, DiscordLogLevel logLevel, sbyte* ptr)
|
static unsafe void DebugWriteDiscordMessage(void* state, DiscordLogLevel logLevel, sbyte* ptr)
|
||||||
{
|
{
|
||||||
ReadOnlySpan<byte> utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)ptr);
|
ReadOnlySpan<byte> utf8 = MemoryMarshal.CreateReadOnlySpanFromNullTerminated((byte*)ptr);
|
||||||
string message = Encoding.UTF8.GetString(utf8);
|
string message = System.Text.Encoding.UTF8.GetString(utf8);
|
||||||
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[{logLevel}]:{message}");
|
System.Diagnostics.Debug.WriteLine($"[Discord.GameSDK]:[{logLevel}]:{message}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -225,18 +224,18 @@ internal static class DiscordController
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe void SetString(sbyte* reference, int length, in ReadOnlySpan<char> source)
|
private static unsafe void SetString(sbyte* reference, int length, string source)
|
||||||
{
|
{
|
||||||
Span<byte> bytes = new(reference, length);
|
Span<sbyte> sbytes = new(reference, length);
|
||||||
bytes.Clear();
|
sbytes.Clear();
|
||||||
Utf8.FromUtf16(source, bytes, out _, out _);
|
Utf8.FromUtf16(source.AsSpan(), MemoryMarshal.Cast<sbyte, byte>(sbytes), out _, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static unsafe void SetString(sbyte* reference, int length, in ReadOnlySpan<byte> source)
|
private static unsafe void SetString(sbyte* reference, int length, in ReadOnlySpan<byte> source)
|
||||||
{
|
{
|
||||||
Span<byte> bytes = new(reference, length);
|
Span<sbyte> sbytes = new(reference, length);
|
||||||
bytes.Clear();
|
sbytes.Clear();
|
||||||
source.CopyTo(bytes);
|
source.CopyTo(MemoryMarshal.Cast<sbyte, byte>(sbytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct DiscordAsyncAction
|
private struct DiscordAsyncAction
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
|
|
||||||
using Snap.Hutao.Core.ExceptionService;
|
using Snap.Hutao.Core.ExceptionService;
|
||||||
using Snap.Hutao.Model.Intrinsic;
|
using Snap.Hutao.Model.Intrinsic;
|
||||||
using Snap.Hutao.Model.Metadata;
|
|
||||||
using Snap.Hutao.Model.Metadata.Avatar;
|
using Snap.Hutao.Model.Metadata.Avatar;
|
||||||
using Snap.Hutao.Model.Metadata.Weapon;
|
using Snap.Hutao.Model.Metadata.Weapon;
|
||||||
using Snap.Hutao.Service.Metadata;
|
using Snap.Hutao.Service.Metadata;
|
||||||
@@ -11,7 +10,6 @@ using Snap.Hutao.Service.Metadata.ContextAbstraction;
|
|||||||
using Snap.Hutao.ViewModel.GachaLog;
|
using Snap.Hutao.ViewModel.GachaLog;
|
||||||
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
using Snap.Hutao.Web.Hoyolab.Hk4e.Event.GachaInfo;
|
||||||
using Snap.Hutao.Web.Hutao.GachaLog;
|
using Snap.Hutao.Web.Hutao.GachaLog;
|
||||||
using System.Collections.Frozen;
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.GachaLog.Factory;
|
namespace Snap.Hutao.Service.GachaLog.Factory;
|
||||||
@@ -24,16 +22,6 @@ namespace Snap.Hutao.Service.GachaLog.Factory;
|
|||||||
[Injection(InjectAs.Scoped, typeof(IGachaStatisticsFactory))]
|
[Injection(InjectAs.Scoped, typeof(IGachaStatisticsFactory))]
|
||||||
internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
||||||
{
|
{
|
||||||
private static readonly FrozenSet<uint> BlueStandardWeaponIdsSet = FrozenSet.ToFrozenSet(
|
|
||||||
[
|
|
||||||
11301U, 11302U, 11306U, 12301U, 12302U, 12305U, 13303U, 14301U, 14302U, 14304U, 15301U, 15302U, 15304U
|
|
||||||
]);
|
|
||||||
|
|
||||||
private static readonly FrozenSet<uint> PurpleStandardWeaponIdsSet = FrozenSet.ToFrozenSet(
|
|
||||||
[
|
|
||||||
11401U, 11402U, 11403U, 11405U, 12401U, 12402U, 12403U, 12405U, 13401U, 13407U, 14401U, 14402U, 14403U, 14409U, 15401U, 15402U, 15403U, 15405U
|
|
||||||
]);
|
|
||||||
|
|
||||||
private readonly IMetadataService metadataService;
|
private readonly IMetadataService metadataService;
|
||||||
private readonly HomaGachaLogClient homaGachaLogClient;
|
private readonly HomaGachaLogClient homaGachaLogClient;
|
||||||
private readonly ITaskContext taskContext;
|
private readonly ITaskContext taskContext;
|
||||||
@@ -45,7 +33,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
|||||||
await taskContext.SwitchToBackgroundAsync();
|
await taskContext.SwitchToBackgroundAsync();
|
||||||
|
|
||||||
List<HistoryWishBuilder> historyWishBuilders = context.GachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
|
List<HistoryWishBuilder> historyWishBuilders = context.GachaEvents.SelectList(gachaEvent => new HistoryWishBuilder(gachaEvent, context));
|
||||||
return CreateCore(taskContext, homaGachaLogClient, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible, options.IsNeverHeldStatisticsItemVisible);
|
return CreateCore(taskContext, homaGachaLogClient, items, historyWishBuilders, context, options.IsEmptyHistoryWishVisible);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GachaStatistics CreateCore(
|
private static GachaStatistics CreateCore(
|
||||||
@@ -54,8 +42,7 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
|||||||
List<Model.Entity.GachaItem> items,
|
List<Model.Entity.GachaItem> items,
|
||||||
List<HistoryWishBuilder> historyWishBuilders,
|
List<HistoryWishBuilder> historyWishBuilders,
|
||||||
in GachaLogServiceMetadataContext context,
|
in GachaLogServiceMetadataContext context,
|
||||||
bool isEmptyHistoryWishVisible,
|
bool isEmptyHistoryWishVisible)
|
||||||
bool isNeverHeldStatisticsItemVisible)
|
|
||||||
{
|
{
|
||||||
TypedWishSummaryBuilderContext standardContext = TypedWishSummaryBuilderContext.StandardWish(taskContext, gachaLogClient);
|
TypedWishSummaryBuilderContext standardContext = TypedWishSummaryBuilderContext.StandardWish(taskContext, gachaLogClient);
|
||||||
TypedWishSummaryBuilder standardWishBuilder = new(standardContext);
|
TypedWishSummaryBuilder standardWishBuilder = new(standardContext);
|
||||||
@@ -75,45 +62,6 @@ internal sealed partial class GachaStatisticsFactory : IGachaStatisticsFactory
|
|||||||
Dictionary<Weapon, int> purpleWeaponCounter = [];
|
Dictionary<Weapon, int> purpleWeaponCounter = [];
|
||||||
Dictionary<Weapon, int> blueWeaponCounter = [];
|
Dictionary<Weapon, int> blueWeaponCounter = [];
|
||||||
|
|
||||||
if (isNeverHeldStatisticsItemVisible)
|
|
||||||
{
|
|
||||||
orangeAvatarCounter = context.IdAvatarMap.Values
|
|
||||||
.Where(avatar => avatar.Quality == QualityType.QUALITY_ORANGE)
|
|
||||||
.ToDictionary(avatar => avatar, _ => 0);
|
|
||||||
purpleAvatarCounter = context.IdAvatarMap.Values
|
|
||||||
.Where(avatar => avatar.Quality == QualityType.QUALITY_PURPLE)
|
|
||||||
.ToDictionary(avatar => avatar, _ => 0);
|
|
||||||
orangeWeaponCounter = context.IdWeaponMap.Values
|
|
||||||
.Where(weapon => weapon.Quality == QualityType.QUALITY_ORANGE)
|
|
||||||
.ToDictionary(weapon => weapon, _ => 0);
|
|
||||||
|
|
||||||
HashSet<Weapon> purpleWeapons = [];
|
|
||||||
foreach (uint weaponId in PurpleStandardWeaponIdsSet)
|
|
||||||
{
|
|
||||||
purpleWeapons.Add(context.IdWeaponMap[weaponId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (GachaEvent gachaEvent in context.GachaEvents)
|
|
||||||
{
|
|
||||||
if (gachaEvent.Type is GachaType.ActivityWeapon)
|
|
||||||
{
|
|
||||||
foreach (uint weaponId in gachaEvent.UpPurpleList)
|
|
||||||
{
|
|
||||||
purpleWeapons.Add(context.IdWeaponMap[weaponId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HashSet<Weapon> blueWeapons = [];
|
|
||||||
foreach (uint weaponId in BlueStandardWeaponIdsSet)
|
|
||||||
{
|
|
||||||
blueWeapons.Add(context.IdWeaponMap[weaponId]);
|
|
||||||
}
|
|
||||||
|
|
||||||
purpleWeaponCounter = purpleWeapons.ToDictionary(weapon => weapon, _ => 0);
|
|
||||||
blueWeaponCounter = blueWeapons.ToDictionary(weapon => weapon, _ => 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pre group builders
|
// Pre group builders
|
||||||
Dictionary<GachaType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
|
Dictionary<GachaType, List<HistoryWishBuilder>> historyWishBuilderMap = historyWishBuilders
|
||||||
.GroupBy(b => b.ConfigType)
|
.GroupBy(b => b.ConfigType)
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) DGP Studio. All rights reserved.
|
// Copyright (c) DGP Studio. All rights reserved.
|
||||||
// Licensed under the MIT license.
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
using Snap.Hutao.Web.Hutao.HutaoAsAService;
|
||||||
using System.Collections.ObjectModel;
|
using System.Collections.ObjectModel;
|
||||||
|
|
||||||
namespace Snap.Hutao.Service.Hutao;
|
namespace Snap.Hutao.Service.Hutao;
|
||||||
|
|
||||||
internal interface IHutaoAsAService
|
internal interface IHutaoAsAService
|
||||||
{
|
{
|
||||||
ValueTask<ObservableCollection<Web.Hutao.HutaoAsAService.Announcement>> GetHutaoAnnouncementCollectionAsync(CancellationToken token = default);
|
ValueTask<ObservableCollection<Announcement>> GetHutaoAnnouncementCollectionAsync(CancellationToken token = default);
|
||||||
}
|
}
|
||||||
@@ -52,13 +52,9 @@
|
|||||||
Grid.Row="0"
|
Grid.Row="0"
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
Text="{Binding DisplayName}"/>
|
Text="{Binding DisplayName}"/>
|
||||||
<Viewbox
|
<Viewbox Grid.Row="1" StretchDirection="DownOnly">
|
||||||
Grid.Row="1"
|
|
||||||
HorizontalAlignment="Left"
|
|
||||||
StretchDirection="DownOnly">
|
|
||||||
<TextBlock
|
<TextBlock
|
||||||
Margin="0,4,0,0"
|
Margin="0,4,0,0"
|
||||||
HorizontalAlignment="Left"
|
|
||||||
Style="{StaticResource TitleTextBlockStyle}"
|
Style="{StaticResource TitleTextBlockStyle}"
|
||||||
Text="{Binding FinishDescription}"/>
|
Text="{Binding FinishDescription}"/>
|
||||||
</Viewbox>
|
</Viewbox>
|
||||||
|
|||||||
@@ -3,8 +3,7 @@
|
|||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:cw="using:CommunityToolkit.WinUI"
|
xmlns:cw="using:CommunityToolkit.WinUI"
|
||||||
xmlns:cwcont="using:CommunityToolkit.WinUI.Controls"
|
xmlns:cwc="using:CommunityToolkit.WinUI.Controls"
|
||||||
xmlns:cwconv="using:CommunityToolkit.WinUI.Converters"
|
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
xmlns:mxi="using:Microsoft.Xaml.Interactivity"
|
||||||
@@ -25,11 +24,6 @@
|
|||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
|
|
||||||
<Page.Resources>
|
<Page.Resources>
|
||||||
<cwconv:DoubleToObjectConverter x:Key="DoubleToOpacityConverter" GreaterThan="0">
|
|
||||||
<cwconv:DoubleToObjectConverter.TrueValue>1.0</cwconv:DoubleToObjectConverter.TrueValue>
|
|
||||||
<cwconv:DoubleToObjectConverter.FalseValue>0.4</cwconv:DoubleToObjectConverter.FalseValue>
|
|
||||||
</cwconv:DoubleToObjectConverter>
|
|
||||||
|
|
||||||
<Flyout x:Key="HutaoCloudFlyout">
|
<Flyout x:Key="HutaoCloudFlyout">
|
||||||
<Grid>
|
<Grid>
|
||||||
<mxi:Interaction.Behaviors>
|
<mxi:Interaction.Behaviors>
|
||||||
@@ -126,12 +120,12 @@
|
|||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||||
Text="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudNotAllowed}"
|
Text="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudNotAllowed}"
|
||||||
TextAlignment="Center"/>
|
TextAlignment="Center"/>
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
Command="{Binding HutaoCloudViewModel.NavigateToSpiralAbyssRecordCommand}"
|
Command="{Binding HutaoCloudViewModel.NavigateToSpiralAbyssRecordCommand}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudSpiralAbyssActivityDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudSpiralAbyssActivityDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudSpiralAbyssActivityHeader}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudSpiralAbyssActivityHeader}"
|
||||||
IsClickEnabled="True"/>
|
IsClickEnabled="True"/>
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
Command="{Binding HutaoCloudViewModel.NavigateToAfdianSkuCommand}"
|
Command="{Binding HutaoCloudViewModel.NavigateToAfdianSkuCommand}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudAfdianPurchaseDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudAfdianPurchaseDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudAfdianPurchaseHeader}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloudAfdianPurchaseHeader}"
|
||||||
@@ -216,7 +210,6 @@
|
|||||||
<shvc:ItemIcon
|
<shvc:ItemIcon
|
||||||
Badge="{Binding Badge}"
|
Badge="{Binding Badge}"
|
||||||
Icon="{Binding Icon}"
|
Icon="{Binding Icon}"
|
||||||
Opacity="{Binding Count, Converter={StaticResource DoubleToOpacityConverter}}"
|
|
||||||
Quality="{Binding Quality}"/>
|
Quality="{Binding Quality}"/>
|
||||||
<Border
|
<Border
|
||||||
HorizontalAlignment="Right"
|
HorizontalAlignment="Right"
|
||||||
@@ -335,14 +328,14 @@
|
|||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
cw:Effects.Shadow="{ThemeResource CompatCardShadow}">
|
||||||
<Grid HorizontalAlignment="Center" Style="{StaticResource GridCardStyle}">
|
<Grid HorizontalAlignment="Center" Style="{StaticResource GridCardStyle}">
|
||||||
<cwcont:ConstrainedBox AspectRatio="1080:533">
|
<cwc:ConstrainedBox AspectRatio="1080:533">
|
||||||
<shci:CachedImage
|
<shci:CachedImage
|
||||||
HorizontalAlignment="Center"
|
HorizontalAlignment="Center"
|
||||||
VerticalAlignment="Center"
|
VerticalAlignment="Center"
|
||||||
CornerRadius="{ThemeResource ControlCornerRadius}"
|
CornerRadius="{ThemeResource ControlCornerRadius}"
|
||||||
Source="{Binding SelectedHistoryWish.BannerImage}"
|
Source="{Binding SelectedHistoryWish.BannerImage}"
|
||||||
Stretch="UniformToFill"/>
|
Stretch="UniformToFill"/>
|
||||||
</cwcont:ConstrainedBox>
|
</cwc:ConstrainedBox>
|
||||||
<Border Grid.ColumnSpan="2" Background="{ThemeResource DarkOnlyOverlayMaskColorBrush}"/>
|
<Border Grid.ColumnSpan="2" Background="{ThemeResource DarkOnlyOverlayMaskColorBrush}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
@@ -514,35 +507,35 @@
|
|||||||
Style="{StaticResource SubtitleTextBlockStyle}"
|
Style="{StaticResource SubtitleTextBlockStyle}"
|
||||||
Text="{shcm:ResourceString Name=ViewPageGachaLogHint}"/>
|
Text="{shcm:ResourceString Name=ViewPageGachaLogHint}"/>
|
||||||
<StackPanel Margin="0,24,0,0" Spacing="{StaticResource SettingsCardSpacing}">
|
<StackPanel Margin="0,24,0,0" Spacing="{StaticResource SettingsCardSpacing}">
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogRefreshAction}"
|
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogRefreshAction}"
|
||||||
Command="{Binding RefreshBySTokenCommand}"
|
Command="{Binding RefreshBySTokenCommand}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogRefreshBySTokenDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogRefreshBySTokenDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogRefreshBySToken}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogRefreshBySToken}"
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||||
IsClickEnabled="True"/>
|
IsClickEnabled="True"/>
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogRefreshAction}"
|
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogRefreshAction}"
|
||||||
Command="{Binding RefreshByWebCacheCommand}"
|
Command="{Binding RefreshByWebCacheCommand}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogRefreshByWebCacheDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogRefreshByWebCacheDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogRefreshByWebCache}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogRefreshByWebCache}"
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||||
IsClickEnabled="True"/>
|
IsClickEnabled="True"/>
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogInputAction}"
|
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogInputAction}"
|
||||||
Command="{Binding RefreshByManualInputCommand}"
|
Command="{Binding RefreshByManualInputCommand}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInputDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInputDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInput}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogRefreshByManualInput}"
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||||
IsClickEnabled="True"/>
|
IsClickEnabled="True"/>
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogImportAction}"
|
ActionIconToolTip="{shcm:ResourceString Name=ViewPageGachaLogImportAction}"
|
||||||
Command="{Binding ImportFromUIGFJsonCommand}"
|
Command="{Binding ImportFromUIGFJsonCommand}"
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogImportDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogImportDescription}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogImportHeader}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogImportHeader}"
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}"
|
HeaderIcon="{shcm:FontIcon Glyph=}"
|
||||||
IsClickEnabled="True"/>
|
IsClickEnabled="True"/>
|
||||||
<cwcont:SettingsCard
|
<cwc:SettingsCard
|
||||||
Description="{shcm:ResourceString Name=ViewPageGachaLogRecoverFromHutaoCloudDescription}"
|
Description="{shcm:ResourceString Name=ViewPageGachaLogRecoverFromHutaoCloudDescription}"
|
||||||
FlyoutBase.AttachedFlyout="{StaticResource HutaoCloudFlyout}"
|
FlyoutBase.AttachedFlyout="{StaticResource HutaoCloudFlyout}"
|
||||||
Header="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloud}"
|
Header="{shcm:ResourceString Name=ViewPageGachaLogHutaoCloud}"
|
||||||
@@ -553,7 +546,7 @@
|
|||||||
<shcb:ShowAttachedFlyoutAction/>
|
<shcb:ShowAttachedFlyoutAction/>
|
||||||
</mxic:EventTriggerBehavior>
|
</mxic:EventTriggerBehavior>
|
||||||
</mxi:Interaction.Behaviors>
|
</mxi:Interaction.Behaviors>
|
||||||
</cwcont:SettingsCard>
|
</cwc:SettingsCard>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -559,15 +559,6 @@
|
|||||||
OffContent="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleOff}"
|
OffContent="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleOff}"
|
||||||
OnContent="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleOn}"/>
|
OnContent="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleOn}"/>
|
||||||
</cwc:SettingsCard>
|
</cwc:SettingsCard>
|
||||||
<cwc:SettingsCard
|
|
||||||
Description="{shcm:ResourceString Name=ViewPageSettingNeverHeldItemVisibleDescription}"
|
|
||||||
Header="{shcm:ResourceString Name=ViewPageSettingNeverHeldItemVisibleHeader}"
|
|
||||||
HeaderIcon="{shcm:FontIcon Glyph=}">
|
|
||||||
<ToggleSwitch
|
|
||||||
IsOn="{Binding AppOptions.IsNeverHeldStatisticsItemVisible, Mode=TwoWay}"
|
|
||||||
OffContent="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleOff}"
|
|
||||||
OnContent="{shcm:ResourceString Name=ViewPageSettingEmptyHistoryVisibleOn}"/>
|
|
||||||
</cwc:SettingsCard>
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
</Border>
|
</Border>
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
using Snap.Hutao.Core.Setting;
|
using Snap.Hutao.Core.Setting;
|
||||||
using Snap.Hutao.Service;
|
using Snap.Hutao.Service;
|
||||||
using Snap.Hutao.Service.Announcement;
|
using Snap.Hutao.Service.Abstraction;
|
||||||
using Snap.Hutao.Service.Hutao;
|
using Snap.Hutao.Service.Hutao;
|
||||||
using Snap.Hutao.View.Card;
|
using Snap.Hutao.View.Card;
|
||||||
using Snap.Hutao.View.Card.Primitive;
|
using Snap.Hutao.View.Card.Primitive;
|
||||||
|
|||||||
@@ -19,12 +19,10 @@ internal sealed partial class PandaClient
|
|||||||
|
|
||||||
public async ValueTask<Response<UrlWrapper>> QRCodeFetchAsync(CancellationToken token = default)
|
public async ValueTask<Response<UrlWrapper>> QRCodeFetchAsync(CancellationToken token = default)
|
||||||
{
|
{
|
||||||
// Use 12 (zzz) instead of 4 (gi) temporarily to get legacy game token
|
GameLoginRequest options = GameLoginRequest.Create(4, HoyolabOptions.DeviceId40);
|
||||||
GameLoginRequest options = GameLoginRequest.Create(12, HoyolabOptions.DeviceId40);
|
|
||||||
|
|
||||||
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
||||||
.SetRequestUri(ApiEndpoints.QrCodeFetch)
|
.SetRequestUri(ApiEndpoints.QrCodeFetch)
|
||||||
.SetHeader("x-rpc-device_id", HoyolabOptions.DeviceId40)
|
|
||||||
.PostJson(options);
|
.PostJson(options);
|
||||||
|
|
||||||
Response<UrlWrapper>? resp = await builder
|
Response<UrlWrapper>? resp = await builder
|
||||||
@@ -36,11 +34,10 @@ internal sealed partial class PandaClient
|
|||||||
|
|
||||||
public async ValueTask<Response<GameLoginResult>> QRCodeQueryAsync(string ticket, CancellationToken token = default)
|
public async ValueTask<Response<GameLoginResult>> QRCodeQueryAsync(string ticket, CancellationToken token = default)
|
||||||
{
|
{
|
||||||
GameLoginRequest options = GameLoginRequest.Create(12, HoyolabOptions.DeviceId40, ticket);
|
GameLoginRequest options = GameLoginRequest.Create(4, HoyolabOptions.DeviceId40, ticket);
|
||||||
|
|
||||||
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
||||||
.SetRequestUri(ApiEndpoints.QrCodeQuery)
|
.SetRequestUri(ApiEndpoints.QrCodeQuery)
|
||||||
.SetHeader("x-rpc-device_id", HoyolabOptions.DeviceId40)
|
|
||||||
.PostJson(options);
|
.PostJson(options);
|
||||||
|
|
||||||
Response<GameLoginResult>? resp = await builder
|
Response<GameLoginResult>? resp = await builder
|
||||||
|
|||||||
@@ -79,7 +79,6 @@ internal sealed partial class PassportClient2
|
|||||||
|
|
||||||
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
HttpRequestMessageBuilder builder = httpRequestMessageBuilderFactory.Create()
|
||||||
.SetRequestUri(ApiEndpoints.AccountGetSTokenByGameToken)
|
.SetRequestUri(ApiEndpoints.AccountGetSTokenByGameToken)
|
||||||
.SetHeader("x-rpc-device_id", HoyolabOptions.DeviceId40)
|
|
||||||
.PostJson(data);
|
.PostJson(data);
|
||||||
|
|
||||||
Response<LoginResult>? resp = await builder
|
Response<LoginResult>? resp = await builder
|
||||||
|
|||||||
Reference in New Issue
Block a user